msforest / notebook

好记性不如烂笔头,记录知识的点点滴滴。
https://github.com/msforest/notebook/wiki
0 stars 0 forks source link

async2.6.1源码分析之waterfall #26

Open msforest opened 6 years ago

msforest commented 6 years ago

waterfall

定义很简单,只用一个函数就解决了,比看parallel轻松多了。

export default function waterfall(tasks, callback) {
    callback = once(callback || noop);
    if (!Array.isArray(tasks)) return callback(new Error('First argument to waterfall must be an array of functions'));
    if (!tasks.length) return callback();
    var taskIndex = 0;

    // 类似于自动执行器,和co模块功能相同,都是用于自动执行“async”后面的状态
    function nextTask(args) {
        var task = wrapAsync(tasks[taskIndex++]);  //对函数进行包装,判断函数是否是异步函数,同asyncify
        task(...args, onlyOnce(next));  // 如果args是空数组,就不会占据一个参数占位符,所以第一次调用时,参数只有一个onlyOnce(next)返回的函数
    }

    function next(err, ...args) {
        if (err === false) return       // https://github.com/caolan/async/issues/1064 false表达的是终止程序流程;对于更多的情况通常是传null/undefined,这里使用了一种技巧。<del>这里算是一个漏洞,不知道是有意为之还是疏忽大意,尽管发生概率不大</del>
        if (err || taskIndex === tasks.length) {
            return callback(err, ...args);
        }
        nextTask(args);
    }

    nextTask([]);
}

export default function once(fn) {
    return function (...args) {
        if (fn === null) return;
        var callFn = fn;
        fn = null;
        callFn.apply(this, args);
    };
}

export default function onlyOnce(fn) {
    return function (...args) {
        if (fn === null) throw new Error("Callback was already called.");
        var callFn = fn;
        fn = null;
        callFn.apply(this, args);
    };
}

function isAsync(fn) {
    return fn[Symbol.toStringTag] === 'AsyncFunction';  //这里没看懂,什么情况下会为true?? 已解决
}
function wrapAsync(asyncFn) {
    return isAsync(asyncFn) ? asyncify(asyncFn) : asyncFn;
}

onceonlyOnce 没看出来有什么区别,都只是为了保证fn不为空;即使在被返回的函数里切断了fn的关系,并不能使得下一次调用相同的fn等于null;我认为切断关系是为了尾调用优化的机制。once是为了保证fn在所在的词法作用域(waterfall)中只被调用一次,类似地,onlyOnce也是为了保证fn在所在的词法作用域(nextTask)中只被调用一次。

error===false可以起到提前退出流程控制的作用,但是会使得程序挂起,因为callback无法执行

msforest commented 6 years ago
function isAsync(fn) {
    return fn[Symbol.toStringTag] === 'AsyncFunction'; //这里没看懂,什么情况下会为true??
    // return Object.prototype.toString.call(fn) === '[object AsyncFunction]'
}

oh, i know.:sweat_smile: refer

msforest commented 5 years ago

使用promise自定义waterfall函数

function waterfall(tasks, done) {
  var promise = tasks.reduce((prev, task) => prev.then((value) => task(value)), Promise.then());
  promise.then((value) => done(value));
}