Promise
是 JavaScript 中处理异步操作的重要机制。深入理解 Promise
的使用不仅有助于编写高效、可维护的异步代码,还能避免回调地狱(callback hell)。以下是对 Promise
的深入讲解,涵盖其工作机制、方法和实际应用。
1. Promise
基础结构
Promise
对象代表一个将来可能完成的异步操作(成功或失败)。它有三种状态:
- pending(待定):初始状态,既不是成功(fulfilled)也不是失败(rejected)。
- fulfilled(已兑现):操作成功完成。
- rejected(已拒绝):操作失败。
一旦状态从 pending
变为 fulfilled
或 rejected
,就不会再改变,且可以通过 .then()
和 .catch()
方法获取结果。
2. 创建一个 Promise
一个 Promise
是通过 new Promise()
构造函数创建的,传入的函数需要两个参数:resolve
和 reject
,它们用于改变 Promise
的状态。
const myPromise = new Promise((resolve, reject) => {
// 异步操作
let success = true;
if (success) {
resolve("操作成功"); // fulfilled 状态
} else {
reject("操作失败"); // rejected 状态
}
});
在上面的例子中,根据条件调用 resolve
或 reject
,来表明这个异步操作的结果。
3. Promise
的链式调用
Promise
的最大优势之一是可以链式调用,通过 .then()
来依次处理异步操作的结果。每一个 .then()
之后可以返回一个新的 Promise
,用于进一步处理后续操作。
链式调用示例:
let myPromise = new Promise((resolve, reject) => {
setTimeout(() => resolve("Step 1 完成"), 1000);
});
myPromise
.then((result) => {
console.log(result); // Step 1 完成
return "Step 2 完成";
})
.then((result) => {
console.log(result); // Step 2 完成
return new Promise((resolve) => {
setTimeout(() => resolve("Step 3 完成"), 1000);
});
})
.then((result) => {
console.log(result); // Step 3 完成
});
在这个示例中:
- 第一个
Promise
在 1 秒后完成,返回Step 1 完成
。 - 第一个
.then()
中,返回字符串"Step 2 完成"
。 - 第二个
.then()
返回的是一个新的Promise
,这个Promise
在 1 秒后完成,返回Step 3 完成
。
每个 .then()
都处理上一个 Promise
的返回值,且可以返回新的值或 Promise
。
4. 错误处理 .catch()
Promise
提供了 .catch()
方法用于处理执行过程中产生的错误。如果在链式调用中发生错误,它会跳到最近的 .catch()
并执行。
示例:
let myPromise = new Promise((resolve, reject) => {
setTimeout(() => reject("出错了"), 1000);
});
myPromise
.then((result) => {
console.log(result);
})
.catch((error) => {
console.log("错误处理: " + error); // 错误处理: 出错了
});
在这里,Promise
在 1 秒后通过 reject
返回错误信息,.catch()
会捕获这个错误并处理。
错误传播机制:
如果某个 .then()
或 .catch()
处理过程中抛出了错误,这个错误会沿着链向下传播,直到被 .catch()
捕获。
let myPromise = new Promise((resolve, reject) => {
setTimeout(() => resolve("Step 1 完成"), 1000);
});
myPromise
.then((result) => {
console.log(result);
throw new Error("Step 2 出错了");
})
.catch((error) => {
console.log("错误处理: " + error.message); // 错误处理: Step 2 出错了
});
5. Promise.all()
和 Promise.race()
1. Promise.all()
Promise.all()
接受一个 Promise
数组,当数组中的所有 Promise
都成功时,它才会成功。如果其中有任何一个 Promise
被拒绝,它会立即失败。
let p1 = new Promise((resolve) => setTimeout(() => resolve("p1 完成"), 1000));
let p2 = new Promise((resolve) => setTimeout(() => resolve("p2 完成"), 2000));
let p3 = new Promise((resolve) => setTimeout(() => resolve("p3 完成"), 3000));
Promise.all([p1, p2, p3]).then((results) => {
console.log(results); // ["p1 完成", "p2 完成", "p3 完成"]
});
如果任何一个 Promise
被拒绝:
let p1 = new Promise((resolve, reject) => setTimeout(() => reject("p1 出错"), 1000));
let p2 = new Promise((resolve) => setTimeout(() => resolve("p2 完成"), 2000));
Promise.all([p1, p2]).catch((error) => {
console.log(error); // p1 出错
});
2. Promise.race()
Promise.race()
同样接受一个 Promise
数组,但它只要其中一个 Promise
完成或拒绝,就会立刻返回。
let p1 = new Promise((resolve) => setTimeout(() => resolve("p1 完成"), 1000));
let p2 = new Promise((resolve) => setTimeout(() => resolve("p2 完成"), 2000));
Promise.race([p1, p2]).then((result) => {
console.log(result); // p1 完成
});
6. Promise.allSettled()
Promise.allSettled()
会等待所有的 Promise
都完成(无论是成功还是失败),然后返回每个 Promise
的结果。
let p1 = new Promise((resolve) => setTimeout(() => resolve("p1 完成"), 1000));
let p2 = new Promise((resolve, reject) => setTimeout(() => reject("p2 出错"), 2000));
Promise.allSettled([p1, p2]).then((results) => {
console.log(results);
// 输出:
// [
// { status: "fulfilled", value: "p1 完成" },
// { status: "rejected", reason: "p2 出错" }
// ]
});
7. Promise.finally()
finally()
方法用于在 Promise
执行结束后,无论 Promise
的状态是 fulfilled
还是 rejected
,都会执行的回调函数。它不会改变 Promise
的值或状态。
let p1 = new Promise((resolve, reject) => {
setTimeout(() => reject("操作失败"), 1000);
});
p1.catch((error) => {
console.log(error); // 操作失败
}).finally(() => {
console.log("操作已完成,无论成功与否"); // 操作已完成,无论成功与否
});
总结
- Promise 用于简化异步操作,避免回调地狱。
- 通过链式调用
.then()
可以顺序执行多个异步操作。 .catch()
用于捕获和处理异步操作中的错误。Promise.all()
和Promise.race()
可以用于并行处理多个Promise
,分别在所有完成或第一个完成时返回结果。Promise.allSettled()
和finally()
提供了更加灵活的错误处理和收尾操作。
通过这些特性,Promise
使得异步代码更加可读、易于维护,并且错误处理变得更加集中和清晰。