手写Promise完整版+静态方法all和race
手写Promise是面试常考考点,个人认为背后主要考察的点是对于上下文环境this和作用域(闭包)的深刻理解(还有一个很重要的点bind,apply,call强制改变this指向并且使函数this不再由运行时决定),才能判断出某个位置的this是什么,某个函数可否实现普通函数和箭头函数的替换...等等
class myPromise{
static PENDING = 'pending';
static FULFILLED = 'fulfilled';
static REJECTED = 'rejected';
constructor(executor_function){//重要:constructor中的this指向的一定是新建的实例
this.state = myPromise.PENDING;
this.value = null;
this.callbackArr=[];
try{
executor_function(this.resolve.bind(this) , this.reject.bind(this));//牢记:以回调形式使用函数作为参数的时候,这个函数内部的上下文this默认为module.export()/window,所以一定要显示绑定this
}catch(err){
this.reject(err);
}
}
resolve(value){
if(this.state === myPromise.PENDING){
this.state = myPromise.FULFILLED;
this.value = value;
setTimeout(()=>{
this.callbackArr.map(funcItem=>{
funcItem.onFulfilledFunc(this.value);
})
})
}
}
reject(error){
if(this.state === myPromise.PENDING){
this.state = myPromise.REJECTED;
this.value = error;
setTimeout(()=>{
this.callbackArr.map(funcItem=>{
funcItem.onRejectedFunc(this.value)
})
})
}
}
then(onFulfilled,onRejected){
//这个then函数的上下文this,仍然是“调用者”promise,因为调用情况是promise.then(),所以then函数作用域中的this只能是上一个promise实例
if(typeof onFulfilled !=='function'){
onFulfilled = function(value){
return value;
}
}
if(typeof onRejected !=='function'){
onRejected = function(error){
return error;
}
}
let thenPromise = new myPromise((res,rej)=>{
if(this.state === myPromise.PENDING){
//如果上一个promise还未被解决,需要把onFulfilled和onRejected挂起,等待上一个promise状态变为解决的时候,作为回调函数执行
this.callbackArr.push({
onFulfilledFunc:(value)=>{
this.parse(thenPromise,onFulfilled(value),res,rej);
},
onRejectedFunc:(err)=>{
this.parse(thenPromise,onRejected(err),res,rej);
}
})
}
if(this.state === myPromise.FULFILLED){
//如果上一个promise状态变成了fulfilled,then中就执行onFulfilled函数
setTimeout(()=>{this.parse(thenPromise,onFulfilled(this.value),res,rej)});
}
if(this.state === myPromise.REJECTED){
//如果上一个promise状态变成了rejected,then中就执行onRejected函数
setTimeout(()=>{this.parse(thenPromise,onRejected(this.value),res.rej)});
}
})
return thenPromise;
}
catch(onRejected){
if(typeof onRejected !=='function'){
onRejected = (onRejected)=>{
return onRejected;
}
}
let catchPromise = new myPromise((res,rej)=>{
if(this.state=myPromise.REJECTED){
setTimeout(()=>{this.parse(thenPromise,onRejected(this.value),res.rej)});
}
if(this.state=myPromise.FULFILLED){
setTimeout(()=>{this.parse(thenPromise,onFulfilled(this.value),res,rej)});
}
if(this.state=myPromise.PENDING){
this.callbackArr.push({
onFulfilledFunc:(value)=>{
this.parse(catchPromise,onFulfilled(value),res,rej);
},
onRejectedFunc:(value)=>{
this.parse(catchPromise,onRejected(value),res,rej);
}
})
}
})
return catchPromise
}
parse(promise,result,resolve,reject){
if(promise === result){
throw new Error('then返回的Promise不能和onFulfilled产生的this一样');
}
try{
if(result instanceof myPromise){
return result.then(resolve,reject);
}else{
return resolve(result);
}
}catch(err){
return reject(err);
}
}
static resolve(value){
return new myPromise((res,rej)=>{
if(value instanceof myPromise){
value.then(res,rej);
}else{
res(value);
}
})
}
static reject(err){
return new myPromise((res,rej)=>{
rej(err);
})
}
static all(promiseArr){
let resArr=[];
let promiseLen = promiseArr.length;
let fulfilledLen = 0;
return new myPromise((res,rej)=>{
for(let i=0;i<promiseLen;i++){
let promiseNow = promiseArr[i];
myPromise.resolve(promiseNow).then((value)=>{
fulfilledLen++;
resArr[i] = value;
//我感觉这里很关键,在then()这个事件循环微任务中判断promiseArr数组是否都获得结果并存入resArr里,而不是在同步代码for循环中判断。
if(fulfilledLen===promiseLen) res(resArr);
//这里还体现了块级作用域和闭包的知识点,因为for内是块级作用域且变量i是用let声明的,所以then的参数(回调函数)才能记住此时的i是多少,能看到常考的同步代码先于异步代码执行,导致同步代码中i变量若不使用闭包,将会被覆盖,导致异步代码中i变化的知识点。如果一定要用var声明i变量(函数作用域),则需要构建立即执行函数(IIFE)来产生基于函数作用域的闭包
})
.catch((err)=>{
rej(err);
})
}
})
}
static race(promiseArr){
return new myPromise((res,rej)=>{
for(let i=0;i<promiseArr.length;i++){
let promiseNow = promiseArr[i];
myPromise.resolve(promiseNow).then((value)=>{
res(value)
})
.catch((err)=>{
rej(err);
})
}
})
}
}
同时,Promise.then()中的回调函数会被作为微任务处理(异步处理)的理解也很必要,以下用一段代码给出案例,并用手写的myPromise代码的关联部分给出解释
Promise.resolve(8).then(res=>{console.log(res);return Promise.resolve(9);}).then(res=>console.log(res,'1'));
Promise.resolve(5).then(res=>{console.log(res);return 10;}).then(res=>console.log(res,'2'));
/*
结果:
8
5
10 2
9 1
*/
8 和 5 的顺序很好理解,他们都位于两个Promise链的第一个then回调中,因此先打印8,后打印5,但是为什么接下来的第二个then调用中,先打印10 2,再打印9 1呢?
原因是then方法中,如果onFulfilled(正确时的回调函数)返回了Promise类型时,then返回的Promise需要再“等待”一个Promise解析成then的时间,就算这个Promise解析是同步的,但是调用then就属于异步操作(then中回调都用setTimeout包裹),所以第二个Promise链的最后一个then要多等一次事件循环才能执行。
parse(promise,result,resolve,reject){
if(promise === result){
throw new Error('then返回的Promise不能和onFulfilled产生的this一样');
}
try{
if(result instanceof myPromise){
return result.then(resolve,reject);//注意这里,相当于在Promise链上多加了一个then方法,而每执行一个then方法就要调用一次setTimeout,parse本身就是在setTimeout的回调中执行的
}else{
return resolve(result);
}
}catch(err){
return reject(err);
}
}