[4-2] 异步编程与事件循环 · 异步编程方案的演进 (Callbacks to Promises)

2 阅读2分钟

所属板块:4. 异步编程与事件循环

记录日期:2026-03-xx
更新:遇到 Promise 输出题或手写题时补充

1. 回调函数(Callback)的时代与痛点

最早的异步方案就是回调函数:

fs.readFile('file.txt', (err, data) => {
  // 处理数据
});

两大致命痛点

  • 回调地狱(Callback Hell):代码向右横向发展,层层嵌套,难以阅读和维护
  • 控制反转:回调函数的执行权交给第三方(比如 AJAX 库),信任缺失,错误处理非常麻烦

这就是 Promise 诞生的背景——它把“回调”变成了“可链式操作的对象”。

2. Promise 的基础与规范(Promises/A+)

Promise 有三种且只能三种状态(单向不可逆):

  • pending(进行中,默认状态)
  • fulfilled / resolved(成功)
  • rejected(失败)

关键边界

  • new Promise(executor) 中的 executor 函数是同步立即执行的
  • .then() / .catch() 的回调是异步的(推入微任务队列,见 [4-1]
new Promise((resolve, reject) => {
  console.log("1");           // 同步
  resolve("success");
}).then(res => {
  console.log("2");           // 微任务,后执行
});
console.log("3");

输出:1 → 3 → 2

3. 链式调用(Chaining)与值穿透

.then() 必须返回一个新的 Promise,实现链式调用:

Promise.resolve(1)
  .then(res => res * 2)      // 返回新 Promise
  .then(res => res + 3)
  .then(res => console.log(res));   // 5

值穿透(重要): 如果 .then() 传入的不是函数(比如 .then(123)),值会直接透传给下一个 .then()

异常穿透: 任何 .then() 中抛出错误或返回 rejected 的 Promise,都会直接跳到后面的 .catch()

4. Promise 核心 API 对比与实战场景

API特点适用场景失败处理方式
Promise.all全部成功才成功,顺序保持原数组并发请求所有数据一个失败就整体失败
Promise.race谁先完成听谁的请求超时控制最快的一个决定状态
Promise.allSettled无论成功失败都返回所有结果(ES2020)相互独立的批量操作不短路
Promise.any只要有一个成功就成功(ES2021)多节点容灾请求全部失败才失败
Promise.resolve/reject快速创建已决的 Promise统一返回格式-

示例(并发控制经典写法):

// 并发请求 + 保持结果顺序
const urls = ['/api1', '/api2', '/api3'];
Promise.all(urls.map(url => fetch(url)))
  .then(responses => Promise.all(responses.map(r => r.json())))
  .then(data => console.log(data));

5. 小结 & 复习时的“演进视角”

  • 回调 → Promise 解决了“地狱”和“控制反转”两大问题
  • 核心是状态机 + 微任务调度(结合 [4-1] 的事件循环理解)
  • 掌握了 Promise 的链式调用和静态 API,async/await 的底层就很好理解了

下一篇文章会进入 [4-3]:终极异步解决方案(Generator + async/await 的本质、await 在 Event Loop 中的表现、并发优化与错误处理)。

返回总目录:戳这里