【前端】ES6 引入的异步编程解决方案Promise 详解
Promise 详解
1. 基本概念
- 定义:Promise 是 ES6 引入的异步编程解决方案,表示一个异步操作的最终完成(或失败)及其结果值。
- 核心作用:替代回调函数,解决“回调地狱”问题,提供更清晰的异步流程控制。
2. 核心状态
Promise 有三种状态:
状态 | 描述 |
---|---|
pending | 初始状态,既未成功也未失败。 |
fulfilled | 操作成功完成,Promise 结果可通过 .then() 获取。 |
rejected | 操作失败,错误信息可通过 .catch() 获取。 |
3. 核心方法
// 基本语法
const promise = new Promise((resolve, reject) => {// 异步操作setTimeout(() => {const success = true;if (success) {resolve("成功"); // 改变为 fulfilled} else {reject("失败"); // 改变为 rejected}}, 1000);
});// 使用 then/catch
promise.then((value) => console.log(value)) // 成功时执行.catch((error) => console.error(error)) // 失败时执行.finally(() => console.log("结束")); // 无论成功失败均执行
4. 核心特性
(1) 链式调用(Chaining)
promise.then((result) => {console.log(result); // 第一个 thenreturn result * 2; // 返回值传递给下一个 then}).then((newResult) => console.log(newResult)); // 第二个 then
(2) 错误传递
- 若某一层
.then()
抛出错误或调用reject
,后续.then()
会跳过,直接进入最近的.catch()
。
promise.then(() => {throw new Error("出错了"); // 触发错误}).then(() => console.log("不会执行")).catch((err) => console.error(err)); // 捕获错误
(3) 微任务队列
- Promise 的回调(
.then
/.catch
)会被放入微任务队列,优先于宏任务(如setTimeout
)执行:
setTimeout(() => console.log("宏任务"), 0); // 后执行
Promise.resolve().then(() => console.log("微任务")); // 先执行
5. 静态方法
方法 | 作用 |
---|---|
Promise.all() | 等待所有 Promise 完成,返回成功结果数组,若有一个失败则立即返回错误。 |
Promise.race() | 哪个 Promise 先完成,就返回其结果(成功或失败)。 |
Promise.resolve() | 将现有值或 Promise 转换为 Promise 对象。 |
Promise.reject() | 直接生成一个 rejected 状态的 Promise。 |
// 示例:Promise.all
Promise.all([p1, p2, p3]).then((results) => console.log(results)) // [result1, result2, result3].catch((err) => console.error(err));// 示例:Promise.race
Promise.race([slowPromise, fastPromise]).then((result) => console.log("最快完成的结果:", result));
6. 异常处理
- 未捕获的 Rejection:若 Promise 抛出错误但未被
.catch()
捕获,会触发全局事件unhandledrejection
:window.addEventListener('unhandledrejection', (event) => {console.error('未捕获的错误:', event.reason);event.preventDefault(); // 阻止默认的控制台报错 });
7. async/await 语法糖
async/await
是基于 Promise 的语法糖,使异步代码更接近同步写法:
async function fetchData() {try {const response = await fetchAPI(); // 等待 Promise 完成console.log(response.data);} catch (error) {console.error(error);} finally {console.log("完成"); // 可选}
}
8. 常见陷阱
-
过早 resolve/reject:
如果在 Promise 构造函数中直接调用resolve
/reject
,会立即执行,而非等待异步操作:// 错误示例:resolve 立即执行 new Promise((resolve) => {resolve("提前完成"); // 这里会立即 resolvesetTimeout(() => console.log("延迟操作"), 1000); });
-
遗忘 error 处理:
未在链式调用中添加.catch()
可能导致错误未被捕获。 -
this 指向问题:
在.then()
中使用箭头函数可避免this
丢失:const obj = {asyncMethod() {return new Promise(resolve => resolve("数据"));},process() {this.asyncMethod().then(data => console.log(this)) // `this` 可能为 undefined} }; // 解决方案:绑定 this 或使用箭头函数
9. 最佳实践
- 避免嵌套回调:使用链式调用或
async/await
。 - 统一错误处理:在顶层添加
.catch()
或全局监听unhandledrejection
。 - 合理拆分 Promise:将复杂逻辑拆分为多个小 Promise,便于调试。
- 结合 async/await 提升可读性:
async function example() {const data = await fetchAPI(); // 等待完成再继续processData(data); }
10. 与 Callback 的对比
特性 | Promise | Callback |
---|---|---|
可读性 | 链式调用更清晰 | 嵌套回调易形成“回调地狱” |
错误处理 | 集中通过 .catch() 处理 | 需在每个回调中单独处理错误 |
控制流 | 支持 Promise.all 等批量操作 | 需手动管理多个回调的完成状态 |
兼容性 | 需 polyfill 旧版浏览器 | 全局可用,无需额外支持 |
总结
Promise 是现代 JavaScript 异步编程的核心工具,通过清晰的状态管理和链式调用,极大提升了代码的可维护性。结合 async/await
,可以进一步简化异步逻辑,避免回调地狱,是处理异步操作的首选方案。