这一小节是2013年那段时间的“历史经典”,朴灵作者详细介绍了当时几个主流的异步流程控制库:async、Step、wind,以及一个重要的底层机制——尾触发与nextTick。这些库的共同目标都是进一步消灭回调地狱,让串行、并行、限流等复杂流程更易表达。
现在(2025年)来看,这些库基本被 async/await 取代了,但了解它们有两大价值:
- 很多老项目、npm包还在用(尤其是async库)。
- 能帮助你理解现代async/await的设计灵感来源。
4.4.1 尾触发与nextTick
作者先解释一个关键概念:尾触发(tail trigger)。
- 在同步函数里,最后一步往往是“触发下一个动作”。
- 异步环境下,直接调用下一个会造成深层嵌套。
- 解决思路:把“下一个动作”推到事件队列尾部(用process.nextTick或setImmediate),避免阻塞当前执行流。
这其实就是微任务的雏形,为后面的库铺路。
4.4.2 async库(最经典的一个,至今仍在广泛使用)
async是当时最流行的流程控制库(作者强烈推荐)。
核心API:
- series:串行执行任务,前一个完成才执行下一个
- parallel:并行执行所有任务,全部完成调用最终回调
- waterfall:串行,且前一个任务的结果传给下一个
- auto:复杂依赖图(支持依赖注入)
- whilst/until:循环控制
示例:串行读取多个文件(waterfall)
const async = require('async');
const fs = require('fs');
async.waterfall([
(callback) => fs.readFile('a.txt', 'utf8', callback),
(dataA, callback) => {
console.log('a:', dataA);
fs.readFile('b.txt', 'utf8', callback);
},
(dataB, callback) => {
console.log('b:', dataB);
callback(null, '最终结果');
}
], (err, result) => {
if (err) return console.error(err);
console.log('完成:', result);
});
并行示例:
async.parallel([
(cb) => fs.readFile('a.txt', 'utf8', cb),
(cb) => fs.readFile('b.txt', 'utf8', cb)
], (err, results) => {
console.log(results[0], results[1]);
});
优点:API丰富、控制粒度细、社区活跃。
4.4.3 Step库
Step库的设计更“线性”,代码看起来像同步:
var Step = require('step');
Step(
function readA() {
fs.readFile('a.txt', 'utf8', this);
},
function process(err, dataA) {
if (err) throw err;
console.log(dataA);
return '处理结果';
},
function readB(err, result) {
if (err) throw err;
fs.readFile('b.txt', 'utf8', this);
}
);
内部用this()作为下一个步骤的回调,自动捕获异常。
4.4.4 wind库(国内特色)
Wind.js是国内开发者尤雨溪(Vue作者)早期作品,尝试用“编译器”实现类似generator的效果(接近现在的async/await):
var readFile = eval(Wind.compile("async", function () {
var a = $await(fs.readFileAsync('a.txt', 'utf8'));
var b = $await(fs.readFileAsync('b.txt', 'utf8'));
console.log(a, b);
}));
readFile().start();
当时很惊艳,但需要预编译,普及度不高。
现代视角(2025年)
这些库的核心思想都被ES2017的async/await完美吸收:
async function main() {
try {
const a = await fs.promises.readFile('a.txt', 'utf8');
const b = await fs.promises.readFile('b.txt', 'utf8');
console.log(a, b);
} catch (err) {
console.error(err);
}
}
main();
async/await = waterfall + try/catch + Promise 的终极融合。