这是前端/Node.js 开发者必须掌握的历史脉络,看懂它,你就彻底明白为什么 async/await 是最终形态,也能彻底理解“一切都可能是异步”的设计根源。
时代 1:回调函数(Callback)—— 混沌时代(ES5 及以前)
核心特点
- 异步完成后,通过函数调用通知结果
- 所有异步逻辑都写在嵌套函数里
- 简单,但嵌套一多就彻底失控
代码示例(回调地狱 Callback Hell)
假设你要依次做 3 件事:
- 获取用户信息
- 根据用户ID获取订单
- 根据订单ID获取商品详情
// 回调嵌套 = 回调地狱
getUser(function(err, user) {
if (err) throw err;
getOrder(user.id, function(err, order) {
if (err) throw err;
getGoods(order.id, function(err, goods) {
if (err) throw err;
console.log(goods);
// 还能继续嵌套...
});
});
});
致命缺点
- 横向嵌套(金字塔代码),可读性极差
- 异常处理混乱(每个回调都要判断 err)
- 无法复用、无法取消、无法并行
- 代码难以维护,被称为回调地狱
时代 2:Promise —— 秩序时代(ES6 2015)
核心特点
- 把异步操作封装成对象,解决嵌套问题
- 用
.then()链式调用,代码变平 - 统一异常处理
.catch() - 支持并行
Promise.all
Promise 是中间层解决方案,不是语法糖,是语言原生类。
代码示例(链式调用)
// 扁平化,告别嵌套
getUser()
.then(user => getOrder(user.id))
.then(order => getGoods(order.id))
.then(goods => console.log(goods))
.catch(err => console.error('统一捕获异常', err));
优点
- 代码变平,不再横向嵌套
- 统一异常处理
- 支持并行/串行/竞赛等异步流程
缺点
- 依然是函数式链式写法,和同步代码思维不同
- 流程控制复杂(循环、条件判断在 then 里很别扭)
- 代码还是不够像自然逻辑
时代 3:Generator + co —— 过渡时代(ES6 2015)
这是async/await 的前身,你只需要知道:
- Generator 可以暂停函数
- 配合 co 库可以自动执行异步
- 写法已经接近同步,但需要库支持、语法繁琐
// 写法接近同步,但需要库,原生不支持自动执行
co(function* () {
const user = yield getUser();
const order = yield getOrder(user.id);
const goods = yield getGoods(order.id);
console.log(goods);
});
它证明了:用同步写法写异步是可行的,于是催生了最终形态。
时代 4:async/await —— 终极时代(ES2017)
核心特点
- 语言原生支持,运行时级特性
- 完全同步写法,执行异步逻辑
- 基于 Promise,但写法更自然
- 完美适配 JS “一切皆异步”的设计前提
代码示例(最终形态)
// 像读小说一样写异步
async function getDetail() {
try {
const user = await getUser();
const order = await getOrder(user.id);
const goods = await getGoods(order.id);
console.log(goods);
} catch (err) {
console.error(err);
}
}
为什么是终极形态?
- 符合人类思维:从上到下顺序执行
- 原生支持暂停/恢复(运行时能力)
- 同步/异步异常统一处理(try/catch)
- 兼容所有 Promise API
- 让 JS 真正做到:不用关心是否异步,写法完全一致
一张表看懂整个进化史
| 方案 | 写法 | 异常处理 | 可读性 | 地位 |
|---|---|---|---|---|
| 回调 | 嵌套金字塔 | 分散混乱 | ⭐ | 历史淘汰 |
| Promise | .then() 链式 | 统一 catch | ⭐⭐⭐ | 基础支撑 |
| Generator | yield + co | 一般 | ⭐⭐⭐⭐ | 过渡方案 |
| async/await | 同步写法 | try/catch | ⭐⭐⭐⭐⭐ | 最终标准 |
核心结论
- JS 天生单线程 → 必须异步 → 异步是默认模式
- 回调太烂 → Promise 解决嵌套 → 但不够自然
- async/await 是运行时原生特性,专门为 JS 异步模型设计
- 它的设计前提:我不管你是不是异步,我都按同步写法处理
→ 这就是 “一切都可能是异步的” 真正含义
总结
- 回调:解决了异步,但制造了地狱
- Promise:规范化异步,铺平代码
- async/await:语言原生支持,用同步写法完美适配 JS 异步本质