从回调地狱到 Promise.then,再到 async/await
回调:你自己管理“下一步”:框架帮你管理回调顺序:语法帮你把回调“铺平成同步代码”📌本质一直没变,变的是“人和回调之间的距离”。
从回调地狱到 Promise.then,再到 async/await
——异步代码是如何一步步“变好读”的
在学习 JavaScript 异步时,几乎所有人都会经历三个阶段:
- 一层一层的回调,看不懂
.then()链式调用,好像清楚了async / await,终于像同步代码了
很多教程会告诉你:
“async/await 解决了回调问题”
但这句话其实并不严谨。
这篇文章不讲结论式口号,而是顺着历史与动机,一步一步解释:
异步代码为什么会变成今天这个样子。
一、最原始的异步:回调函数
先从一段经典代码说起:
getUser(id, user => {
getOrders(user, orders => {
getDetail(orders, detail => {
console.log(detail);
});
});
});
很多初学者看到这段代码的第一反应是:
- 这是不是递归?
- 是不是 getUser 里面“必须”执行 getOrders?
- 为什么一层套一层?
1️⃣ 这段代码到底在表达什么?
用人话翻译,其实只有一句:
等用户数据拿到以后,再去拿订单;
等订单拿到以后,再去拿详情;
最后处理结果
这不是函数调用顺序,
而是异步任务之间的依赖关系。
2️⃣ 为什么会“看起来像一层套一层”?
因为你在用回调描述“下一步要做什么”:
getUser(id, callback1);
// callback1 的含义是:
// “将来 user 拿到之后,请执行这里的代码”
当下一步依赖上一步的结果时,只能写成:
上一步(结果 => {
下一步(结果2 => {
再下一步(...)
});
});
📌
嵌套不是因为调用顺序,而是因为依赖关系。
3️⃣ 回调本身有没有错?
没有。
真正的问题是:
- 可读性差
- 嵌套深
- 错误处理分散
- 不符合人类“从上到下”的阅读习惯
这就是所谓的 回调地狱(Callback Hell)。
二、Promise:不是消灭回调,而是“管理回调”
为了解决回调地狱,Promise 被引入了。
1️⃣ 用 Promise 重写上面的逻辑
getUser(id)
.then(user => getOrders(user))
.then(orders => getDetail(orders))
.then(detail => console.log(detail))
.catch(err => console.error(err));
你会发现几件事:
- 不再一层套一层
- 执行顺序从上到下
- 错误可以统一 catch
2️⃣ 那回调还在吗?
在,而且很明显:
.then(user => ...)
.then(orders => ...)
这些 仍然是回调函数。
区别在于:
回调从“你自己嵌套管理”,
变成了“Promise 帮你按顺序调度”。
3️⃣ Promise 真正解决的是什么?
Promise 并没有解决“异步”本身,而是:
- 把异步过程抽象成一个对象(Promise)
- 用状态机(pending / fulfilled / rejected)管理流程
- 把回调拉平成链式结构
📌
Promise = 回调 + 状态管理 + 链式调度
三、async / await:让异步“看起来像同步”
再往后,就是大家最熟悉的写法了:
async function load() {
const user = await getUser(id);
const orders = await getOrders(user);
const detail = await getDetail(orders);
console.log(detail);
}
第一次看到这段代码,很多人会觉得:
“这不是同步代码吗?”
1️⃣ async/await 到底做了什么?
一句话说明白:
async/await 只是 Promise 的语法糖
你写的:
const user = await getUser(id);
在底层等价于:
getUser(id).then(user => {
// 从这里继续执行后面的代码
});
📌
“继续执行后面的代码”本身,就是一个回调。
只是:
- 这个回调不是你写的
- 是 JS 引擎自动帮你生成的
2️⃣ async/await 消灭了什么?
❌ 没有消灭回调
❌ 没有消灭 Promise
❌ 没有消灭异步
它真正消灭的是:
人脑理解异步流程的成本
四、then 和 await:是不是随便选?
不是。
它们的能力等价,但使用场景不同。
1️⃣ 什么时候用 await?
👉 写业务流程的时候
async function page() {
const user = await getUser(id);
const orders = await getOrders(user);
render(orders);
}
原因很简单:
- 符合人类顺序思维
- 易读、易维护
- try/catch 像同步异常
2️⃣ 什么时候用 .then()?
👉 写底层工具 / 库 / 可组合逻辑时
function getUser(id) {
return fetch(`/user/${id}`).then(r => r.json());
}
让调用者自己决定:
- 用
.then() - 还是
await
3️⃣ 并发场景:then 和 Promise 才是核心
const [a, b] = await Promise.all([
fetchA(),
fetchB()
]);
📌
你会发现:
await 从来没有取代 Promise
它只是“等待 Promise 结果”的方式
五、一个重要但常被忽略的结论
异步世界里,不可能没有回调
因为:
- IO 何时完成不由你决定
- 网络何时返回不可预测
- 事件何时发生不可预测
你能做的只有一件事:
提前告诉系统:事情完成以后,你要做什么
这,就是回调。
Promise 和 async/await,只是让这件事更优雅。
六、整条演进路线的一句话总结
- 回调:你自己管理“下一步”
- Promise.then:框架帮你管理回调顺序
- async/await:语法帮你把回调“铺平成同步代码”
📌
本质一直没变,变的是“人和回调之间的距离”。
更多推荐



所有评论(0)