一、Promise是什么?
Promsie 是异步编程的一种解决方案,本质上是一个函数返回的对象。它的构造函数是同步执行的,但 then 方法是异步的。所以Promise创建后里面的函数会立即执行,而构造函数中的resolve和reject只有在第一次执行时才有效。也就是说Promise的状态一旦改变就不能再变。
二、Promise的作用
Promise主要是用来解决回调地狱,通过.then方法使得代码成链式调用,方便以后的维护和使用。
1.回调地狱
回调地狱(Callback Hell)也被称为“金字塔之Doom”,是指在使用异步编程时,由于多个回调函数的嵌套使用而导致代码变得复杂、难以阅读和维护。
回调地狱的特点:
- 深度嵌套: 因为异步操作的数量不断增加,回调函数不断嵌套,形成一个向右扩展的倒金字塔形状。
- 代码可读性差: 随着嵌套的函数越来越多,降低了代码的可读性和维护性。
- 错误处理困难: 因为每个回调函数都有可能发生错误,增加了出错的可能性。
2.链式调用
Promise的链式调用是通过 then 方法,将多个异步操作串联起来,并且每个 then 方法都可以返回一个新的 Promise,从而实现链式调用。
Promise链式调用的基本原理
- 在每次调用 .then 方法后都会返回一个新的 Promise。
- 如果是返回一个普通值,那么下一个 then 方法会把这个值作为参数。
- 如果返回一个 Promise ,那么下一个 then 方法会等待这个 Promise 被解决(fulfilled或rejected)后再执行。
错误处理
在链式调用中,任何一步出错都会跳过后续所有的 then 方法,直接进入最近的 catch 方法来处理错误。所以,在 then 方法中如果出现了错误,可以通过抛出异常或返回一个被拒绝的 Promise 来触发错误处理逻辑。
三、Promise的状态
Promise 总共有三种状态,且一定处于某种状态之中:
- 待定(pending):初始状态,既没有成功,也没有失败。但一旦确定了状态,就永远不会变。
- 成功(fulfilled):意味着操作成功。
- 拒绝(rejected):意味着操作失败。
状态转换
- pending -> fulfilled: 当异步操作成功时,Promise 的状态会从 Pending 变成 fulfilled,并传递一个值给处理函数。
- pending ->rejected : 当异步操作失败后,Promise 的状态会变成 rejected,并抛出原因给处理函数。
四、Promise的并发
Promise 提供了四种静态方法来处理异步任务的并发:
- Promise.all(): 接受一个 Promsie 实例数组,并返回新的 Promise 。当所有的 Promsie 完成后才会被解决,并将所有Promsie 的结果作为一个数组传递给 .then() 方法中的回调函数。如果任意一个 Promise 被拒绝后,那么整个 Promise.all() 就会被立刻拒绝,并抛出错误信息。
const promise1 = Promise.resolve(3);
const promise2 = 42;
const promise3 = new Promise((resolve, reject) => {
setTimeout(resolve, 100, 'foo');
});
Promise.all([promise1, promise2, promise3]).then(values => {
console.log(values); // 输出: [3, 42, "foo"]
}).catch(error => {
console.error(error);
- Promise.allSettled(): 功能与 Promise.all()相似,只是不管是否成功或失败,都会返回一个对象数组。每个对象都包含属性(fulfilled 或 rejected)和相应的值或原因。
const promise1 = Promise.resolve(3);
const promise2 = new Promise((resolve, reject) => setTimeout(reject, 100, 'Error'));
const promise3 = 42;
Promise.allSettled([promise1, promise2, promise3]).then(results => {
results.forEach(result => {
if (result.status === 'fulfilled') {
console.log(`Fulfilled: ${result.value}`);
} else {
console.error(`Rejected: ${result.reason}`);
}
});
// 输出:
// Fulfilled: 3
// Rejected: Error
// Fulfilled: 42
});
- Promise.race(): 接受一个 Promsie 实例数组,并返回新的 Promise 。而这个新的 Promsie 会在第一个输入的 Promise 被解决时立即解决,并返回结果或错误信息。
const promise1 = new Promise((resolve) => setTimeout(resolve, 500, 'one'));
const promise2 = new Promise((resolve) => setTimeout(resolve, 100, 'two'));
Promise.race([promise1, promise2]).then(value => {
console.log(value); // 输出: "two"
}).catch(error => {
console.error(error);
});
- Promise.any(): 和 Promise.race()类似,新返回的 Promise 会在任意一个输入的 Promsie 完成后立即解决,只有所有输入的 Promsie 被拒绝后才会被拒绝。是因为 AggregateError 对象,包含了所有拒绝的原因。
const promise1 = new Promise((_, reject) => setTimeout(reject, 100, 'one'));
const promise2 = new Promise((_, reject) => setTimeout(reject, 200, 'two'));
const promise3 = Promise.resolve('three');
Promise.any([promise1, promise2, promise3]).then(value => {
console.log(value); // 输出: "three"
}).catch(error => {
if (error instanceof AggregateError) {
console.error("All promises were rejected", error.errors);
}
});
要注意的是,JS本质上是单线程的,所以无论何时只有一个任务会被执行,是因为控制权不断在Promise 之间切换,才使得Promsie的执行看起来像并发的。
五、Promise的静态方法
Promise 提供了几个静态方法来解决异步操作。其中Promise.all()、Promise.allSettled()、Promise.race()、Promise.any()已经介绍过了,现在让我们看看剩下的几个。
- Promise.resolve(): 将传入的值转换成 Promise,如果本身就是 Promise 则直接返回该 Promise;如果值是一个 thenable(有.then 方法的对象),那会将其包装成 Promsie 返回;否则返回以该值为结果的新 Promise。
// 返回一个已经 fulfilled 的 Promise
Promise.resolve('Success').then(value => {
console.log(value); // 输出: "Success"
});
// 处理 thenable 对象
const thenable = {
then: function(resolve, reject) {
resolve('Resolved');
}
};
Promise.resolve(thenable).then(value => {
console.log(value); // 输出: "Resolved"
});
- Promise.reject(): 会返回一个已经被拒绝的 Promise,并返回原因。
Promise.reject(new Error('Failed')).catch(error => {
console.error(error.message); // 输出: "Failed"
});
六、Promise的构造函数
Promsie 的构造函数是创建 Promise 实例的主要方法。它把执行器(executor)函数作为参数,这个执行器函数是会立即执行的,通常用于启动一步操作。
执行器函数有两个参数:resolve 和 reject,这些都是函数。
- resolve: 异步操作成功执行时调用,传入的数据作为 promise 的结果。
- reject: 异步操作失败时调用,把传入的原因作为 Promsie 的拒绝原因。
构造函数说明
首先,用 new 创建一个 Promise 实例,其状态是 pending(待定)。Promise 在创建后就会立即执行,执行器函数会启动异步操作,并根据结果调用 resolve 或 reject。调用 resolve(value) ,Promise 状态会变成 fulfilled(已实现),并返回值。调用reject(reason),状态会变成 rejected(已拒绝),并返回原因。
七、总结
这篇文章主要写了 Promise 在面试中的一些考点,涉及具体使用、注意要点和原理性知识,但由于本人掌握有限,要是有什么不足,还请指出。如果觉得不错,还请点赞收藏。