JavaScript 关于Promise
Promise 对象是 JavaScript 中的一种用于处理异步操作的机制,首次引入于 ES6 标准。它的诞生旨在解决传统回调函数所带来的复杂性和弊端,特别是所谓的“回调地狱”。在早期的 JavaScript 中,异步操作通常通过嵌套回调函数来实现,例如 AJAX 请求、定时器、文件读取等。然而,随着异步逻辑的复杂化,代码逐渐变得难以维护,出现了层层嵌套的现象,阅读和调试都非常困难。
为了解决这个问题, ES6 推出了Promise
对象 🎉
什么是 Promise ?
从本质上说,Promise 是一个代理对象(Proxy),它代表一个尚未完成但最终会完成的异步操作的结果。Promise 的设计思想是:将异步操作封装为一个对象,并提供统一的 API 来处理其结果,无论是成功(resolved
)还是失败(rejected
)。
就像是你和你老婆之间的“承诺”。比如说,你老婆答应你明天会帮你洗碗。在这之前,你不知道他会真的洗碗还是出什么意外,这个状态就是“等待中”(Pending)。等到第二天,如果他真的把碗洗了了,那他就“成功”(Fulfilled)兑现了承诺,你可以得到一个干净的厨房,这就是结果;如果他因为某些原因没做成,比如心情不好,或者耍赖,那他就“失败”(Rejected)了,会过一段时间还是过很久时间告诉你失败的原因都是有可能的。
在编程中,Promise 也是这样,它是一个代表异步操作最终完成或失败的对象。你可以通过它来知道一个异步操作是成功了还是失败了,并且拿到相应的结果或错误信息。
它有三种可能的状态:
- Pending(进行中):Promise 刚创建时的状态,表示操作尚未完成。
- Fulfilled(已完成):异步操作成功完成,并返回结果。
- Rejected(已失败):异步操作失败,并返回错误原因。 重要的是,
Promise
的状态是不可逆的。一旦从Pending
转为Fulfilled
或Rejected
,它的状态和结果就会被固定,不会再发生变化。
在没有 Promise 之前
在 JavaScript 的发展初期,异步操作主要通过回调函数实现。一个典型的例子是使用回调处理 AJAX 请求:
function fetchData(callback) {
setTimeout(() => {
callback(null, "数据加载成功");
}, 1000);
}
fetchData((err, data) => {
if (err) {
console.error(err);
} else {
console.log(data);
}
});
当异步逻辑复杂化时,例如需要连续执行多个异步任务(任务之间可能存在依赖关系),回调函数就会变成嵌套的形式:
getUser((err, user) => {
if (err) return handleError(err);
getOrders(user, (err, orders) => {
if (err) return handleError(err);
processOrders(orders, (err, result) => {
if (err) return handleError(err);
console.log("处理结果", result);
});
});
});
这种代码形式被称为回调地狱
,不仅可读性差,而且容易引发错误。例如,错误处理逻辑需要在每一层重复定义,稍有不慎就可能遗漏,导致 🐛 Bug 难以排查。
Promise 的基本用法
通过 Promise
构造函数创建一个 Promise 对象,传入一个执行器函数,该函数包含两个参数:resolve
和 reject
。
const promise = new Promise((resolve, reject) => {
const success = true;
if (success) {
resolve("操作成功!"); // 成功时调用 resolve
} else {
reject("操作失败!"); // 失败时调用 reject
}
});
使用 .then() 和 .catch() 处理结果
then(onFulfilled, onRejected)
:当 Promise 被Fulfilled
或Rejected
时,分别调用对应的回调函数。catch(onRejected)
:用于捕获 Promise 的拒绝结果。
promise
.then(result => {
console.log(result); // 输出:操作成功!
})
.catch(error => {
console.error(error); // 如果失败,输出错误信息
});
使用 .finally() 处理完成后的操作
finally
无论 Promise 成功还是失败都会执行,通常用于清理资源。
promise
.then(result => {
console.log(result);
})
.catch(error => {
console.error(error);
})
.finally(() => {
console.log("操作已结束!");
});
Promise 静态方法
1. Promise.all()
Promise.all
接收一个包含多个 Promise 的数组(或者类数组对象),返回一个新的 Promise。当数组中的所有 Promise 都成功时,Promise.all
返回一个包含所有结果的数组。如果其中有一个 Promise 失败,它会直接返回这个失败的 Promise,整个 Promise.all
也会变为失败状态。
const promise1 = Promise.resolve(1);
const promise2 = Promise.resolve(2);
const promise3 = Promise.resolve(3);
Promise.all([promise1, promise2, promise3])
.then((results) => {
console.log(results); // [1, 2, 3]
})
.catch((error) => {
console.error("有一个失败了:", error);
});
全有或全无:只要有一个 Promise 被 reject,整个 Promise.all 就会立即 reject,不管其他的 Promise 是否已经完成。
如果数组为空,Promise.all([]) 会立即返回一个 resolved 状态的 Promise,结果是空数组。
2. Promise.allSettled()
Promise.allSettled
会等待所有 Promise 完成,不管是成功还是失败。它返回一个包含每个 Promise 结果的数组,每个结果对象有两个属性:
status:表示是
fulfilled
(成功)还是rejected
(失败)。value 或 reason:分别存储成功的值或失败的原因。
const promise1 = Promise.resolve("成功");
const promise2 = Promise.reject("失败");
Promise.allSettled([promise1, promise2])
.then((results) => {
console.log(results);
// [
// { status: "fulfilled", value: "成功" },
// { status: "rejected", reason: "失败" }
// ]
});
不会因为某个 Promise 失败而终止,所有的 Promise 都会被等待完成。
适合用在希望获取每个异步任务结果的场景,而不是中途失败就停下的场景。
3. Promise.race()
Promise.race 也是接受一个包含多个 Promise 的数组,但是它只会返回 最先完成
的 Promise 的结果,不管是成功还是失败
。
const promise1 = new Promise((resolve) => setTimeout(() => resolve("A"), 100));
const promise2 = new Promise((resolve) => setTimeout(() => resolve("B"), 200));
Promise.race([promise1, promise2])
.then((result) => {
console.log(result); // "A" (因为 promise1 更快完成)
})
.catch((error) => {
console.error("最先完成的 Promise 失败了:", error);
});
适合用在需要“竞争”的场景,比如发出多个请求,取第一个返回的结果。
如果第一个完成的 Promise 是失败的,Promise.race 的结果也会是失败。
4. Promise.any()
Promise.any
会返回第一个成功完成的 Promise。如果所有 Promise 都失败了,它会返回一个 AggregateError
,表示所有 Promise 都失败。
const promise1 = Promise.reject("失败1");
const promise2 = Promise.resolve("成功");
const promise3 = Promise.reject("失败2");
Promise.any([promise1, promise2, promise3])
.then((result) => {
console.log(result); // "成功"
})
.catch((error) => {
console.error("所有 Promise 都失败了:", error);
});
和 Promise.race
的区别在于,Promise.any
只关心成功的结果,失败的 Promise 会被忽略。
如果所有 Promise 都失败了,catch 中会接收到一个 AggregateError
,其中包含所有失败原因的数组。
5. Promise.resolve() 和 Promise.reject()
Promise.resolve
用来快速创建一个已完成 fulfilled
状态的 Promise,并返回指定的值。
Promise.reject
用来快速创建一个已失败 rejected
状态的 Promise,并返回指定的失败原因。
const resolvedPromise = Promise.resolve(42);
const rejectedPromise = Promise.reject("出错了");
resolvedPromise.then((value) => {
console.log(value); // 42
});
rejectedPromise.catch((error) => {
console.error(error); // "出错了"
});
如果传入的值是一个 Promise,Promise.resolve
会直接返回这个 Promise。
Promise.resolve
如果传入的是普通值,它会返回一个立即完成的 Promise,Promise.reject
则返回一个失败的 Promise。
用 Promise.resolve
和 Promise.reject
可以快速创建测试用的 Promise,但不要用它们直接替代真正的异步操作