async.js 关于 waterfall 的文档: caolan.github.io/async/v3/do…
什么是 waterfall
依次运行数组中的函数,然后传递该函数的结果到下一个函数。
如果任何一个函数执行失败并把失败传入回调函数,后边的函数一律不执行,并且立即调用第二个参数的函数。
# 官方示例
# 第一个参数是函数的数组,第二个参数是最终的回调函数【可不传】
# 之所以使用 callback 函数作为数组中的函数的参数,是因为在这些函数中可能要执行一些异步的任务,
# 如果直接用 return 返回值的方式,就不必用 waterfall 了,一个个顺序执行这些函数即可。
# 而且 waterfall 这种方式也兼容同步的任务,把返回值传入 callback 函数即可。
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'
});
编码实现【用aa实现】
/**
* 定义 waterfall 序列执行任务包装函数
*
* @param tasks
* @param cb
* @returns {Promise<*>}
*/
async function waterfall(tasks, cb) {
// 如果用户不传入第二个参数,这里补一个默认的
cb = cb || function (err){
if (err) {
console.error(err)
} else {
console.log('done')
}
}
// 如果没有任务,直接结束
if (!Array.isArray(tasks) || !tasks.length) {
return cb()
}
// 任何一个任务失败直接进入 catch, 后续函数也就不会执行了
try {
// 初始化传入每一个函数的数据,第一个函数没有人传给他数据,所以是空的
let args = []
// 使用for循环以及 await 去逐个执行任务,并得到其调用 callback 时需要传入的返回值
for (let i = 0; i < tasks.length; i++) {
let fn = tasks[i]
// 判断不是函数时直接失败
if ((typeof fn) !== 'function') {
throw 'tasks must be functions'
}
args = await new Promise((resolve, reject) => {
// 用户数组中函数的 callback 其实是我们这个 waterfall 函数给定义的
// 对用户来说 callback 是个黑盒子,他只需要知道调用它,就可以中断流程或把想要的值传入下一个函数
// callback 函数用 ...data 的方式收集用户调用函数是传入的参数【因为参数数目不确定】。
// callback 函数的作用就是把上一步的失败 reject 掉或 data 给resolve 出来给 args。
fn.call(this, ...args, function (err, ...data) {
console.log(`err: ${err} data: ${data}`)
if (err) {
reject(err)
} else {
resolve(data)
}
})}
)
}
// 执行完成后,执行最终的 cb
cb(null)
} catch(err){
cb(err)
}
}
测试
/**
* 调用测试
*
*/
waterfall([
function(callback) {
console.log('函数1')
callback(null, 'one', 'two');
},
function(arg1, arg2, callback) {
console.log('函数2')
// arg1 now equals 'one' and arg2 now equals 'two'
callback(null, 'three');
},
function(arg1, callback) {
console.log('函数3')
// arg1 now equals 'three'
callback(null, 'done');
}
], function (err, result) {
// result now equals 'done'
if (err) {
console.error(err)
} else {
console.log('用户传入cb done')
}
})