async.waterfall 的简单实现是什么?

What is a simple implementation of async.waterfall?

我正在使用 async library, and want to make sure I understand how they're doing things internally; however, I'm stuck on async.waterfall (implementation here) 中的一些函数。实际实现使用库中的其他函数,没有太多经验,我发现很难理解。

有人可以在不担心优化的情况下提供一个非常简单实现瀑布功能的实现吗?可能相当于 this answer.

来自 the docs,瀑布的描述:

Runs the tasks array of functions in series, each passing their results to the next in the array. However, if any of the tasks pass an error to their own callback, the next function is not executed, and the main callback is immediately called with the error.

一个例子:

async.waterfall([
    function(callback) {
        callback(null, 'one', 'two');
    },
    function(arg1, arg2, callback) {
      // arg1 now equals 'one' and arg2 now equals 'two'
        callback(null, 'three');
    },
    function(arg1, callback) {
        // arg1 now equals 'three'
        callback(null, 'done');
    }
], function (err, result) {
    // result now equals 'done'    
});

好吧,这是一个通过排队来链接函数的简单实现。

首先是函数:

function waterfall(arr, cb){} // takes an array and a callback on completion

现在,我们需要跟踪数组并迭代它:

function waterfall(arr, cb){
    var fns = arr.slice(); // make a copy
}

让我们从处理传递的数组和空数组开始,通过添加一个额外的参数,这样我们就可以传递名为 result:

的结果
function waterfall(arr, cb, result){ // result is the initial result
    var fns = arr.slice(); // make a copy
    if(fns.length === 0){
        process.nextTick(function(){ // don't cause race conditions
            cb(null, result); // we're done, nothing more to do
        });
    }
}

太好了:

waterfall([], function(err, data){
    console.log("Done!");
});

现在,让我们实际处理一下:

function waterfall(arr, cb, result){ // result is the initial result
    var fns = arr.slice(1); // make a copy, apart from the first element
    if(!arr[0]){ // if there is nothing in the first position
        process.nextTick(function(){ // don't cause race conditions
            cb(null, result); // we're done, nothing more to do
        });
        return;
    }
    var first = arr[0]; // get the first function
    first(function(err, data){ // invoke it
         // when it is done
         if(err) return cb(err); // early error, terminate entire call
         // perform the same call, but without the first function
         // and with its result as the result
         waterfall(fns, cb, data); 
    });
}

就是这样!我们基本上通过使用递归来克服无法循环回调的事实。 Here is a fiddle 说明它。

值得一提的是,如果我们用 promise 来实现它,我们可以使用 for 循环。

对于那些喜欢保持简短的人:

function waterfall(fn, done){
   fn.length ? fn.shift()(function(err){ err ? done(err) : waterfall(fn, done) }) :  done();
}