前言
Promise作为异步编程的一种解决方案,无论什么阶段的前端都是须要牢牢把握,在面试中也是属于必问的。
一、高频概念问答
1、你了解的Promise?
<details>
<summary><font color=blue>参考</font></summary>
Promise是ES6中异步编程的一种解决方案;它是为了解决异步链式调用呈现的回调天堂问题;有三种状态pending进行中、fulfilled胜利、rejected失败,且状态一旦扭转不可逆;promise.then()是微工作
</details>
2、Promise罕用办法?
<details>
<summary><font color=blue>参考</font></summary>
Promise.all、Promise.allSettled、Promise.any、Promise.race、Promise.resolve、Promise.reject
</details>
3、你了解的async和await?
<details>
<summary><font color=blue>参考</font></summary>
async是ES7中对于异步操作的解决方案,它是Generator函数的语法糖,解决了promise链式调用的问题,用同步的形式去执行异步;
</details>
4、Promise.all,Promise.race区别
<details>
<summary><font color=blue>参考</font></summary>
Promise.all() 全副promise胜利才算胜利,一个promise就算失败,返回胜利的数据数组,失败抛出最先失败的promise的reason。 Promise.race() 最先的promise实现则返回,promise后果和最先实现的promise统一
</details>
5、执行程序?
难度 ⭐⭐⭐
-
- 执行宏工作的过程中,遇到微工作,顺次退出微工作队列;
-
- 当某个宏工作执行完后,会查看是否有微工作队列;
- 有,执行微工作队列中的所有工作;
- 没有,读取宏工作队列中最后面工作;
-
- 栈空后,再次读取微工作队列里的工作,顺次类推
async function async1() {
console.log('async1 start');
await async2();
console.log('async1 end');
}
async function async2() {
console.log('async2');
}
console.log('script start');
setTimeout(function() {
console.log('setTimeout');
}, 0)
async1();
new Promise(function(resolve) {
console.log('promise1');
resolve();
}).then(function() {
console.log('promise2');
});
console.log('script end');
// 输入后果
script start
async1 start
async2
promise1
script end
async1 end
promise2
setTimeout
难度 ⭐⭐⭐⭐
// 在上题中做了一点改变
async function async1() {
console.log('async1 start');
await async2();
console.log('async1 end');
}
async function async2() {
//async2做出如下更改:
new Promise(function(resolve) {
console.log('promise1');
resolve();
}).then(function() {
console.log('promise2');
});
}
console.log('script start');
setTimeout(function() {
console.log('setTimeout');
}, 0)
async1();
new Promise(function(resolve) {
console.log('promise3');
resolve();
}).then(function() {
console.log('promise4');
});
console.log('script end');
// 输入后果
script start
async1 start
promise1
promise3
script end
promise2
async1 end
promis4
setTimeout
难度 ⭐⭐⭐⭐
console.log('start')
setTimeout(()=>{
console.log("children2")
Promise.resolve().then(()=>{
console.log("children3")
})
},0)
new Promise(function(resolve,reject){
console.log('children4')
setTimeout(()=>{ //重点
console.log("children5")
resolve("children6")
},0 )
}).then(res=>{
console.log("children7")
setTimeout(()=>{
console.log(res)
},0)
})
// 输入后果
start
children4
//第一轮宏工作完结 尝试清空微工作队列 没有微工作
children2
//第二轮宏工作完结 尝试清空微工作队列
children3
//第三轮
children5
children7
children6
二、手撕代码
手写 Promise 和 Promise.all 是面试频率最高的;
1、Promise.all 难度⭐⭐⭐
列举几个关键点:
- 1.return Promise 返回也是Promise
- 2.参数类型判断,传入的参数必须是数组;
- 3.数组元素类型,判断传入的值是否是promise,应用Promise.resolve 办法会把参数转化为promise;
-
4.返回后果程序问题;
function PromiseAll(promiseArray){ return new Promise((resolve,reject)=>{ // !!! 考点1 if(!Array.isArray(promiseArray)){ return reject(new Error('传入的参数必须是数组')) } const result = [] let counter = 0 const promisenNums = promiseArray.length // !!! 考点2 判断传入的值是否是promise //Object.prototype.toString.call==='[object Promise]' for(let i = 0 ;i < promisenNums ; i++){ //Promise.resolve 办法会把参数转化为promise Promise.resolve(promiseArray[i]).then(value=>{ // push会造成数据错乱 用count计数,不必长度判断 // result.push(value) // if(result.length === promisenNums){ // resolve(result) // } //!!! 考点3 promise的程序问题下面的形式疏忽了promise的程序问题 counter++; result[i] = value; if(counter === promisenNums){ resolve(result) } }).catch(e=>reject(e)) } }) }
2、Promise 难度⭐⭐⭐⭐
举荐文章 硬核! 手把手教你写A+标准的Promise
class Promose { constructor(executor){ this.status = 'pending' this.value = null this.reason = null this.onResolvedCallbacks = [] //寄存胜利的回调函数 新加 this.onRejectedCallbacks = [] //寄存失败的回调函数 新加 //reslove函数 const resolve = (value) => { if(this.status === 'pending'){ this.status = 'fulfilled' this.value = value //执行所有的订阅的fulfilled函数 新加 this.onResolvedCallbacks.forEach(fn=>fn()) } } //reject函数 const reject = (reason) => { if(this.status === 'pending'){ this.status = 'rejected' this.reason = reason //执行所有的订阅的rejected函数 this.onRejectedCallbacks.forEach(fn=>fn()) } } try{ executor(resolve,reject) }catch(err){ reject(err) } } then(onFulfilled,onRejected){ //胜利的回调 if(this.status = 'fulfilled'){ onFulfilled(this.value) } //失败的回调 if(this.status = 'rejected'){ onRejected(this.reason) } //如果是期待状态,就把res,rej的解决回调函数放入对应的队列 新加 if(this.status ==='pending'){ //放入胜利回调队列 this.onResolvedCallbacks.push(()=>{ //写额定的逻辑 onFulfilled(this.value) }) //放入失败回调队列 this.onRejectedCallbacks.push(()=>{ //写额定的逻辑 onRejected(this.reason) }) } } }
3、其余手撕 难度⭐⭐⭐
掘金有看到其余大佬的文章,就不反复造车,间接上链接
看了就会,手写 Promise 全副 API 教程
三、大厂真题
1、应用Promise实现每隔1秒输入1,2,3,4?
const arr = [1, 2, 3 , 4]
arr.reduce((p, x) => {
return p.then(() => {
return new Promise(r => {
setTimeout(() => r(console.log(x)), 1000)
})
})
}, Promise.resolve())
2、Promise做缓存?
场景:一个频繁调用接口的办法在多处应用
const cacheMap = new Map(); //用一个Map来存
function enableCache(target, name, descriptor) {
const oldValue = descriptor.value; //拿到原来的办法
descriptor.value = function(...args) { //重写这个办法
//因为申请的参数必定会不一样,所以要定义一个的key
const cacheKey = name + JSON.stringify(args);
if (!cacheMap.get(cacheKey)) {
//为什么要加catch 因为你的申请有可能会报错
const cacheValue = Promise.resolve(oldValue.apply(this, args)).catch(_ => {
cacheMap.set(cacheKey, null) //报错了,设置成null
})
cacheMap.set(cacheKey, cacheValue)
}
return cacheMap.get(cacheKey); //把缓存拿进去即可,无须再去http申请,无须再去申请服务端
}
return descriptor;
}
class PromiseClass {
//装璜器
@enableCache
static async getInfo() {}
}
// 应用
PromiseClass.getInfo()
PromiseClass.getInfo()
PromiseClass.getInfo()
未完 继续补充欠缺
❤️感激浏览
- 如果本文对你有帮忙,不要悭吝你的赞哟,你的「赞」是我前行的能源;
- 欢送关注公众号 【冥想侃前端】 一起学习提高。
发表回复