Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

async2.6.1源码分析之waterfall #26

Open
msforest opened this issue Aug 5, 2018 · 2 comments
Open

async2.6.1源码分析之waterfall #26

msforest opened this issue Aug 5, 2018 · 2 comments

Comments

@msforest
Copy link
Owner

msforest commented Aug 5, 2018

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)中只被调用一次。

  • once用于async库内部保证callback被安全调用一次;onlyOnce用于外部调用async库内部的callback被安全调用一次,如果调用多次,把错误抛给用户。

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

@msforest
Copy link
Owner Author

msforest commented Aug 5, 2018

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

oh, i know.:sweat_smile:
refer

@msforest
Copy link
Owner Author

使用promise自定义waterfall函数

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant