最近在使用到node js的async库的时候,对其waterfall的实现感觉很奇妙,于是看了一下源码:

    async.waterfall = function (tasks, callback) {
        callback = callback || function () {};
        if (!_isArray(tasks)) {
          var err = new Error('First argument to waterfall must be an array of functions');
          return callback(err);
        }
        if (!tasks.length) {
            return callback();
        }
        var wrapIterator = function (iterator) {
            return function (err) {
                if (err) {
                    callback.apply(null, arguments);
                    callback = function () {};
                }
                else {
                    var args = Array.prototype.slice.call(arguments, 1);
                    var next = iterator.next();
                    if (next) {
                        args.push(wrapIterator(next));
                    }
                    else {
                        args.push(callback);
                    }
                    async.setImmediate(function () {
                        iterator.apply(null, args);
                    });
                }
            };
        };
        wrapIterator(async.iterator(tasks))();
    };
 

开始先对参数进行了检查,判断tasks是否是一个function数组。然后使用了一个内部函数wrapIterator封装了实现。wrapIterator的参数带出了async.iterator函数:

 
    async.iterator = function (tasks) {
        var makeCallback = function (index) {
            var fn = function () {
                if (tasks.length) {
                    tasks[index].apply(null, arguments);
                }
                return fn.next(); //这个地方有必要么???
            };
            fn.next = function () {
                return (index < tasks.length - 1) ? makeCallback(index + 1): null;
            };
            return fn;
        };
        return makeCallback(0);
    };


这个函数,其主要实现是其内部函数makeCallback。其功能就是迭代tasks,封装其中的每一个function,让其执行后返回下一个function,以此实现迭代。

接下来,再回到wrapIterator,此function是对iterator的封装。执行后返回的是一个匿名function。其明确的参数只有一个err。当err不为空的时候,直接执行callback function。否则从index为1开始取出参数列表,并把iterator的下一个function包装之后push到args中(如果没有下一个function了则push回调函数)。接下来,则执行当前的iterator,执行的参数是下一个iterator function(作为这一步的回调函数)以及参数(如果当前的iterator被调用时传递了其他参数)。这样在当前iterator中回调下一个iterator,依次迭代执行,直至执行完所有function和callback。