带回调的异步并行行为
async 库具有一个名为 parallel
的便捷方法,该方法接受要运行的异步函数集合以及一个可选的回调函数,在所有函数成功完成后运行该回调函数。
根据文档,它的工作原理如下:
并行运行任务函数集合,无需等待前一个函数完成。如果任何函数将错误传递给其回调,则立即使用错误值调用主回调。任务完成后,结果将作为数组传递给最终回调。
然而,关于在某些用例下回调函数是否被调用,这可能会变得非常令人困惑,我在阅读带有回调地狱的大型遗留代码库时也感到很困惑。请参阅下面的代码和示例输出。
- 所有任务都运行,但不执行回调
import async from 'async';
const task = {
A: true,
B: true,
};
async.parallel(
[
function (done) {
if(task.A) {
console.log('does A');
return;
}
done(null);
},
function (done) {
if(task.B) {
console.log('does B');
return;
}
done(null);
},
],
function (err) {
if (err) {
console.log(err);
return;
}
console.log('does callback');
},
);
/* console output
does A
does B
*/
- 一个任务运行,但没有回调
import async from 'async';
const task = {
A: true,
B: false,
};
async.parallel(
[
function (done) {
if(task.A) {
console.log('does A');
return;
}
done(null);
},
function (done) {
if(task.B) {
console.log('does B');
return;
}
done(null);
},
],
function (err) {
if (err) {
console.log(err);
return;
}
console.log('does callback');
},
);
/* console output
does A
*/
- 无任务运行然后回调运行
import async from 'async';
const task = {
A: false,
B: false,
};
async.parallel(
[
function (done) {
if(task.A) {
console.log('does A');
return;
}
done(null);
},
function (done) {
if(task.B) {
console.log('does B');
return;
}
done(null);
},
],
function (err) {
if (err) {
console.log(err);
return;
}
console.log('does callback');
},
);
/* console output
does callback
*/
- 一个任务通过回调运行
import async from 'async';
const task = {
A: true,
B: false,
};
async.parallel(
[
function (done) {
if(task.A) {
setTimeout(() => {
console.log('does A');
done(null);
}, 100);
return;
}
done(null);
},
function (done) {
if(task.B) {
console.log('does B');
return;
}
done(null);
},
],
function (err) {
if (err) {
console.log(err);
return;
}
console.log('does callback');
},
);
/* console output
does A
does callback
*/
我们可以看到,只有当所有并行函数最终正常调用时,callback
才会被调用。至于错误情况,文档很清楚:
如果任何函数将错误传递给其回调,则会立即使用错误的值调用主回调。
参考资料