了解Promise
2025-08-18 15:42
“当我们学会管理异步,代码世界才会真正清晰。”
为什么需要 Promise?
写 JavaScript 的过程中,最让人头疼的就是“异步”。浏览器请求数据、定时器、文件操作……这些事情都不会立刻返回结果。于是,最常见的解决方案就是回调函数(callback)。但随着逻辑变复杂,回调会层层嵌套,形成著名的“回调地狱”:
JSgetData(function (a) { parseData(a, function (b) { filterData(b, function (c) { saveData(c, function (result) { console.log(result); }); }); }); });
这种结构可读性极差,调试也非常困难。于是,Promise 出现了,它用一种更优雅的方式来管理异步流程。
Promise 是什么?
简单来说,Promise 是一个代表未来结果的容器。 它有三种状态:
pending(进行中)fulfilled(已完成)rejected(已失败) 一旦状态从 pending 变成 fulfilled 或 rejected,就会“锁定”,无法再更改。这种设计保证了异步结果的确定性。
基本用法
创建一个 Promise:
JSconst p = new Promise((resolve, reject) => { setTimeout(() => { resolve("成功的数据"); // 或者 reject("出错了"); }, 1000); }); p.then((data) => { console.log("成功:", data); }).catch((err) => { console.error("失败:", err); }).finally(() => { console.log("完成"); });
和回调对比,Promise 写法显得更加线性,逻辑关系清晰很多。
链式调用
Promise 最大的优势在于可以通过 then 链接多个异步操作:
JSfetch("/api/user") .then((res) => res.json()) .then((user) => fetch(`/api/order/${user.id}`)) .then((res) => res.json()) .then((order) => console.log(order)) .catch((err) => console.error("出错了:", err));
通过 catch,我们可以统一处理链路上的错误,而不需要在每一步都写错误回调。
Promise 的 并发
Promise 提供了提供了四个静态方法来促进异步任务的并发:
| 方法 | 是否等待全部完成 | 失败时行为 | 返回结果 | 典型用途 |
|---|---|---|---|---|
Promise.all() | ✅ 是 | ❌ 任意一个失败就整体 reject | 所有结果的数组 | 并行执行任务,全部成功才继续 |
Promise.allSettled() | ✅ 是 | ✅ 不会 reject | 每个任务的状态与结果 | 即使有失败,也要拿到全部结果 |
Promise.race() | ❌ 否 | ❌ 第一个结束(成功或失败)就返回 | 单个 Promise 的结果 | 比赛场景,如“请求超时控制” |
Promise.any() | ❌ 否 | ✅ 第一个成功的返回,否则报 AggregateError | 第一个成功值 | 容错并发,只要有一个成功就行 |
虽然在上面提了并发,但请注意,JavaScript 本质上是单线程的,因此在任何时刻都只会执行一个任务,尽管控制权在不同 Promise 的回调之间轮流执行,从而使 Promise 的执行看起来像是并发,在 JavaScript 中只有 worker 能实现 并行执行。
并发 和 并行
并发和并行是两种不同的概念, 英文在上面简单提到过所以这里简单解释一下。
| 特性 | 并发 (Concurrency) | 并行 (Parallelism) |
|---|---|---|
| 执行方式 | 逻辑上同时,时间上可能重叠 | 真正同时执行 |
| 线程要求 | 单线程也可以实现 | 需要多线程或多核 CPU |
| JS 中的表现 | Promise、async/await、事件循环 | Worker / Node.js 多线程 |
| 主线程行为 | 依次执行每个回调,但异步任务同时等待 | 多线程同时执行代码 |
Promise.withResolvers()
Promise.withResolvers() 是 ES2024 新增的静态方法,用于快速创建一个可控 Promise。它会返回一个对象 { promise, resolve, reject },可以在外部手动控制 Promise 的状态。
JSlet num = true; const { promise, resolve, reject } = Promise.withResolvers(); promise.then((res) => { console.log(res); }); if (num) { resolve("成功"); } else { reject("失败"); }
旧写法对比:
JSlet resolve, reject; const promise = new Promise((res, rej) => { resolve = res; reject = rej; });
Promise.withResolvers() 比传统写法更简洁,避免了先声明变量再赋值的繁琐步骤。
-
典型应用场景
- 延迟执行 / 事件触发
JSconst { promise, resolve } = Promise.withResolvers(); document.querySelector("#btn").addEventListener("click", () => resolve("按钮被点击")); await promise;- 异步任务队列 / 异步事件系统
- 单元测试:手动控制 Promise 完成时间,更方便测试异步逻辑
实现 Promise
JSclass MyPromise { constructor(executor) { this.state = "pending"; this.value = null; this.reason = null; this.onFulfilled = []; this.onRejected = []; const resolve = (value) => { if (this.state === "pending") { this.state = "fulfilled"; this.value = value; this.onFulfilled.forEach((fn) => fn(value)); } }; const reject = (reason) => { if (this.state === "pending") { this.state = "rejected"; this.reason = reason; this.onRejected.forEach((fn) => fn(reason)); } }; try { executor(resolve, reject); } catch (err) { reject(err); } } then(onFulfilled, onRejected) { if (this.state === "fulfilled") { onFulfilled(this.value); } else if (this.state === "rejected") { onRejected(this.reason); } else { this.onFulfilled.push(onFulfilled); this.onRejected.push(onRejected); } } } // 使用示例 const p = new MyPromise((resolve, reject) => { setTimeout(() => resolve("成功啦!"), 1000); }); p.then((data) => console.log(data));
最后
Promise 并不是最终的答案,但它让我们从回调地狱中解放出来,并为 async/await 打下了基础。可以说,理解 Promise 是理解现代 JavaScript 异步编程的第一步。
换句话说,Promise 让我们以单线程的方式,享受了并行世界的便利。这是我关于 Promise 的第一篇技术笔记。如果你有不同的使用经验或踩过的坑,欢迎交流分享。