async2.6.1源码分析之waterfall
Opened this issue · 2 comments
msforest commented
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;
}
once
和onlyOnce
没看出来有什么区别,都只是为了保证fn
不为空;即使在被返回的函数里切断了fn
的关系,并不能使得下一次调用相同的fn
等于null
;我认为切断关系是为了尾调用优化
的机制。once
是为了保证fn
在所在的词法作用域(waterfall
)中只被调用一次,类似地,onlyOnce
也是为了保证fn
在所在的词法作用域(nextTask
)中只被调用一次。
once
用于async库内部保证callback被安全调用一次;onlyOnce
用于外部调用async库内部的callback被安全调用一次,如果调用多次,把错误抛给用户。
error===false
可以起到提前退出流程控制的作用,但是会使得程序挂起,因为callback
无法执行
msforest commented
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
使用promise自定义waterfall函数
function waterfall(tasks, done) {
var promise = tasks.reduce((prev, task) => prev.then((value) => task(value)), Promise.then());
promise.then((value) => done(value));
}