总结一下之前面试遇到的 Promise 相关的问题:
1. promise 的特点
- 对象的状态不会受外界环境的影响。Promise 对象代表一个异步操作,有三种状态:pending(进行中)、fufilled(已成功)、rejected(已失败),只有异步操作的结果才能决定最后的状态。
- 一旦状态改变,就不会再变,任何时候都可以得到这个结果。 状态的改变只能由 pending -> fulfilled/resolved 和 pending -> rejected。
2. promise 原理
- Promise 对象是一个构造函数,用来生成 Promise 实例,这个实例一旦创建就会立即执行;
- 实例中接收一个执行函数作为参数,这个执行函数有两个函数类型形参(resolve 和 reject);
- resolved 函数的作用是将 Promie 对象的状态从“未完成”到“成功”,在异步函数成功时调用,并将异步操作的结果作为参数传递出去;
- rejected 函数的作用是将 Promise 对象的状态从“未完成”到“失败”,在异步函数失败时调用,并将异步操作报出的错误作为参数传递出去。
- Promise 实例生成后,可以用 then 方法分别指定 resolved 状态和 rejected 状态的回调函数。
3. 如何同时请求三个 url 之后再执行接下来的程序
promise.all().then()
4. promise.all 和 promise.allSettled 的区别
4.1 promise.all
- 接收一个 PromiseList;
- 多个 Promise 任务同时执行;
- 如果全部成功则以数组的方式返回所有 Promise 任务的执行结果;
- 如果有一个 Promise rejected,则只返回 rejected 任务的结果。
const promise1 = Promise.resolve(3);
const promise2 = 42;
const promise3 = new Promise((resolve, reject) => {
setTimeout(resolve, 100, 'foo');
});
const promise4 = Promise.reject(new Error('error'));
Promise.all([promise1, promise2, promise3])
.then((values) => {
console.log(values); // [3, 42, "foo"]
});
Promise.all([promise1, promise2, promise3, promise4])
.catch(err => {
console.log(err); // Error
});
4.2 promise.allSettled
- 接收一个 PromiseList;
- 多个 Promise 任务同时执行;
- 不论 PromiseList 里面的任意一个 promise 是 fulfilled 还是 rejected,都返回一个数组对象,每个对象表示对应的 promise 结果。。
const p1 = Promise.resolve(33);
const p2 = new Promise((resolve) => setTimeout(resolve, 0, 66));
const p3 = 99;
const p4 = Promise.reject(new Error("an error"));
Promise.allSettled([p1, p2, p3, p4]).then((values) => console.log(values));
// [
// { status: 'fulfilled', value: 33 },
// { status: 'fulfilled', value: 66 },
// { status: 'fulfilled', value: 99 },
// { status: 'rejected', reason: Error: an error }
// ]
5. promise.race 和 promise.any 的区别
5.1 promise.race
- 接收一个 promiseList;
- 多个 Promise 任务同时执行;
- 返回最先执行结束的 Promise 任务的结果,不管这个 Promise 是成功还是失败。
const p1 = new Promise((resolve, reject) => setTimeout(resolve, 3000, "promise1"));
const p2 = new Promise((resolve, reject) => setTimeout(reject, 1000, "error promise2 "));
Promise.race([p1, p2]).catch(err => {
console.log('err', err); // err error promise2
})
const p3 = new Promise((resolve, reject) => setTimeout(reject, 3000, "error promise3"));
const p4 = new Promise((resolve, reject) => setTimeout(resolve, 1000, "promise4"));
Promise.race([p3, p4]).then(res => {
console.log('res', res); // res promise4
});
5.2 promise.any
- 接收一个 promiseList;
- 多个 Promise 任务同时执行;
- 只要一个 Promise 执行成功,则返回该执行成功的promise 结果;如果全都失败,则返回
AggregateError
实例,这是Error
的子类,用于把单一的错误集合在一起。
const p2 = new Promise((resolve, reject) => setTimeout(reject, 2000, "error promise2 "));
const p3 = new Promise((resolve, reject) => setTimeout(reject, 3000, "error promise3"));
const p4 = new Promise((resolve, reject) => setTimeout(resolve, 4000, "promise4"));
Promise.any([p2, p3, p4]).then((first) => {
console.log(first); // promise4
});
Promise.any([p2, p3]).catch(err => {
console.log(err); // AggregateError: All promises were rejected
});
6. try...catch... 可以捕获 Promise 异常吗?
不能,try...catch只能捕获同步异常,对于异步异常不能捕获。
promise中的异常可以用 promise.catch
进行处理。
// 方法1
async function run() {
try {
await Promise.reject(new Error("Oops!"));
} catch (error) {
console.log(error.message); // "Oops!"
}
}
run();
// 方法2
Promise.reject(new Error("Oops!")).catch(err => console.log(err)); // Error: Oops!
7.代码题
7.1 实现 Promise.all
Promise.all = function (promiseList = []) {
const p1 = new Promise((resolve, reject) => {
const result = []; // 存储 promiseList 所有的结果
const length = promiseList.length;
let resolvedCount = 0;
promiseList.forEach(p => {
Promise.resolve(p).then(data => {
result.push(data);
resolvedCount++;
if (resolvedCount === length) { // 已经遍历到了最后一个 promise
resolve(result);
}
}).catch(err => {
reject(err);
})
})
});
return p1;
}
问题1:为什么不用用 index,反而单独定义了一个 resolvedCount 变量?
因为有的 promise 执行的慢,有的 promise 执行的快,会出现执行顺序被打乱的问题。
问题2:如果 promiseList 中有常量怎么办?
利用 Promise.resolve
的特性,用这个方法包裹的参数,无论是什么类型,都会返回一个 promise。
7.2 实现红黄绿灯交替闪烁,可查看另一篇文章
7.3 输出以下代码的执行结果①
Promise.resolve().then(() => {
console.log(0);
return Promise.resolve(4);
}).then(res => {
console.log(res);
});
Promise.resolve().then(() => {
console.log(1);
}).then(() => {
console.log(2);
}).then(() => {
console.log(3);
}).then(() => {
console.log(5);
}).then(() => {
console.log(6);
});
这道题中考察到了两个知识点:
-
then 函数会交替执行
如果有多个 fulfilled 状态的 promise 实例,同时执行
then
链式调用,那么每个 promise 的 then 函数会交替执行,这是编译器的优化,防止一个 promise 占据太久时间 -
慢“两拍”
当
then
中返回一个 promise 实例的时候,会出现慢两拍的情况:- 第一拍:promise 需要由 pending 变成 fulfilled
- 第二拍:then 函数挂载到 MicroTaskQueue(参考 事件循环 event loop)
-
结果
综上,最终的执行结果是 0 1 2 3 4 5 6
7.4 输出以下代码的执行结果②
Promise.resolve().then(() => {
console.log(1);
}).catch(() => {
console.log(2);
}).then(() => {
console.log(3);
})
输出结果:1、3、resolved状态的 Promise
7.5 输出以下代码的执行结果③
Promise.resolve().then(() => { // rejected
console.log(1);
throw new Error('error')
}).catch(() => { // resolved
console.log(2);
}).then(() => { // resolved
console.log(3);
})
输出结果:1、2、3、resolved状态的 Promise
7.6 输出以下代码的执行结果④
Promise.resolve().then(() => { // rejected
console.log(1);
throw new Error('error')
}).catch(() => { // resolved
console.log(2);
}).catch(() => {
console.log(3);
})
输出结果:1、2、resolved状态的 Promise
如果后续有遇到,会持续更新,如果有问题,也欢迎一起讨论⌛️~
本网站是一个以CSS、JavaScript、Vue、HTML为核心的前端开发技术网站。我们致力于为广大前端开发者提供专业、全面、实用的前端开发知识和技术支持。 在本网站中,您可以学习到最新的前端开发技术,了解前端开发的最新趋势和最佳实践。我们提供丰富的教程和案例,让您可以快速掌握前端开发的核心技术和流程。 本网站还提供一系列实用的工具和插件,帮助您更加高效地进行前端开发工作。我们提供的工具和插件都经过精心设计和优化,可以帮助您节省时间和精力,提升开发效率。 除此之外,本网站还拥有一个活跃的社区,您可以在社区中与其他前端开发者交流技术、分享经验、解决问题。我们相信,社区的力量可以帮助您更好地成长和进步。 在本网站中,您可以找到您需要的一切前端开发资源,让您成为一名更加优秀的前端开发者。欢迎您加入我们的大家庭,一起探索前端开发的无限可能!