说到promise,学前端的应该都很熟悉了。promise的用处可谓是很大了,解决地狱回调,异步并发任务的执行...
最近感觉用promise就像用框架一下,知道一些API和基本用法,就可以使用promise做一些简单的操作。既然有框架那么就有对应的底层源码,那就深入的学习一下吧。以下仅是个人的思考的写法,参考了一些大佬的思路,如有偏颇请批评指正。
总而言之,手写promise确实不简单,开始写之前可以先将promise的基本用法写上做一个对照。
//promise的基本用法
const p=new Promise((resolve,reject)=>{
resolve(2)
//reject(1)
// setTimeout(()=>{ //模拟异步请求
// console.log(1)
// },0)
})
p.then(res=>{
console.log(res)
},err=>{
console.log(err)
})
首先实现一个基础版的:
- 设置三个状态常量,PENDING,RESOLVED,REJECTED,并且维持单向改变。
- MyPromise接受一个executor函数,有两个方法resolve和reject
- resolve()将状态改为RESOLVED,并且让promise具有一个value
- reject()将状态改为REJECTED,并且让promise具有一个reason
const PENDING='pending'
const RESOLVED='resolved'
const REJECTED='rejected'
function MyPromise(executer){
//初始化状态
this.state=PENDING
this.value=null
this.reason=null
//定义resolve和reject方法
const resolve=(value)=>{
//设置promise的单向状态
if(this.state===PENDING){
this.state=RESOLVED
this.value=value
}
}
const reject=(reason)=>{
//设置promise的单向状态
if(this.state===PENDING){
this.state=REJECTED
this.reason=reason
}
}
try{
executer(resolve,reject)
}catch(reason){
reject(reason)
}
}
接下来就是then方法的实现了,实例可以直接调用then,则说明,then是在构造函数原型上的
- 接受两个函数参数,onFulfilled,onRejected,分别在promise变为RESOLVED,REJECTED执行
- 一个promise可以绑定多个then,并且then可以链式调用
- then方法可以同步调用也可以异步调用
MyPromise.prototype.then=function(onFulfilled,onRejected){
//允许链式调用,返回一个promise
return new MyPromise((resolve,reject)=>{
//this的指向是MyPromise的实例
//由于then里面的方法是异步执行的,
//用setTimeout模拟
switch (this.state) {
case RESOLVED:
// onFulfilled(this.value)
setTimeout(()=>{
const x= onFulfilled(this.value)
resolve(x)
},0)
break;
case REJECTED:
// onRejected(this.reason)
setTimeout(()=>{
const x=onRejected(this.reason)
resolve(x)
},0)
break;
case PENDING:
this.onFulfilledCallbacks.push(()=>{
// onFulfilled(this.value)
setTimeout(()=>{
const x= onFulfilled(this.value)
resolve(x)
},0)
})
this.onRejectedCallbacks.push(()=>{
// onRejected(this.reason)
setTimeout(()=>{
const x=onRejected(this.reason)
resolve(x)
},0)
})
break;
}
})
}
catch和finally的实现都是建立在then的基础上的,finally无论最终promise的状态是成功还是失败
都会执行传入的函数。
//catch和finally
MyPromise.prototype.catch=function(onRejected){
return this.then(null,onRejected)
}
MyPromise.prototype.finally=function(fn){
return this.then(res=>{
fn()
return res
},err=>{
fn()
throw new Error(err)
})
}
同理,MyPromise.resolved和MyPromise.reject也可以依据已将包装好的MyPromise来实现
//MyPromise.resolved和MyPromise.reject
MyPromise.resolve=function(value){
return new MyPromise(resolve=>{
resolve(value)
})
}
MyPromise.reject=function(reason){
return new MyPromise((resolve,reject)=>{
reject(reason)
})
}
最后就剩下MyPromise的all,race,any,allsettled方法,注意这些方法就不在MyPromise的原型对象上,而是直接在MyPromise上
//MyPromise的all,race,any,allsettled
//all
MyPromise.all=function(promises){
return new MyPromise((resolve,reject)=>{
if(promises.length==0){
return resolve([])
}
let results=[]
let length=promises.length
for(let i=0;i<length;i++){
promises[i].then(res=>{
if(++i==length){
resolve(results)
}
results[i]=res
}).catch(err=>{
reject(err)
return
})
}
})
}
//race
MyPromise.race=function(promises){
return new MyPromise((resolve,reject)=>{
for(let i=0;i<promises.length;i++){
promises[i].then(res=>{
resolve(res)
return
}).catch(err=>{
reject(err)
return
})
}
})
}
//any
MyPromise.any=function(promises){
return new MyPromise((resolve,reject)=>{
let results=[]
let length=promises.length
for(let i=0;i<length;i++){
promises[i].then(res=>{
resolve(res)
return
}).catch(err=>{
results[i]=err
if(++i==length){
reject(results)
}
})
}
})
}
//allSettled
MyPromise.allsettled=function(promises){
return new MyPromise((resolve,reject)=>{
let results=[]
for(let i=0;i<promises.length;i++){
promises[i].then(res=>{
results[i]=res
}).catch(err=>{
results[i]=err
}).finally(()=>{
if(++i==promises.length){
resolve(results)
}
})
}
})
}
至此promise里面的常用的API基本已经实现了,总结一下就是,后面四个API里面的代码逻辑差不多,具体到resolve和reject的不同依据不同的期待返回值来定。如果有哪里不正确或者可以优化的地方,欢迎交流探讨!
每天都要进步一点点!加油!