今天带大家实现手写promise啊!加油加油
简介
promise作为处理异步的常见方法,它可以使异步代码看起来像同步一样执行。它用来解决地狱回调问题,使得代码可读性大大增加。
一个 Promise 必然处于以下几种状态之一:
待定(pending):初始状态,既没有被兑现,也没有被拒绝。
已兑现(fulfilled):意味着操作成功完成。
已拒绝(rejected):意味着操作失败。
难点
对于实现手写promise主要有这么几个难点
- 代码的执行顺序问题
- 代码的this指向问题
- 链式调用会出现的递归问题
实现代码
简单promise
首先我们凭直觉应该能写出这样的代码,即promise是一个class,在new promise的过程中其实是在创建这个class的实例。
而这个class中至少应该有这么几个方法:
- constructor :用于接收这个回调函数并执行
- resolve :用于处理状态转变为fulfilled后所需要做的事情
- reject :用于处理状态转变为rejected后所需要做的事情
- then :用于接收状态改变为fulfilled/rejected后的回调函数
需要注意的点在于这段代码,这么写是因为constructor内执行的是个异步函数,它的执行顺序并不确定,如果他是setTimeout(()=>{},0)那么它就比then快,如果不是0秒他就会比then慢,所以这里要分情况讨论
kotlin
then(f1:IResolve,f2?:IReject){
if(this.status=="fulfilled"){
console.log("运行了成功")
f1(this.data)
}else if(this.status=="rejected"){
f2(this.data)
}else if(this.status=="padding"){
this.f1f2.push([f1,f2])
}
}
然后还有一些小的this指向问题,this丢了再用call/bind绑定一下就行了!
然后我们凭借程序员的直觉就能写出下面的代码了!。(❁´◡`❁)
kotlin
type IFn=(resolve:IResolve,reject:IReject)=>void
type IResolve<T=any>=(data:T)=>void
type IReject<T=any>=(data:T)=>void
class Promise2{
private status='pending'
private f1f2:[IResolve,IReject][]=[]
private data:any
constructor(fn:IFn) {
fn.call(this,this.resolve.bind(this),this.reject.bind(this));
}
resolve(data):IResolve{
console.log("运行resolve")
this.status="fulfilled"
this.data=data;
let arriveF1f2=this.f1f2.shift();
if(arriveF1f2){
arriveF1f2[0](data);
}
return
}
reject(data):IReject{
this.status="rejected"
this.data=data;
let arriveF1f2=this.f1f2.shift();
if(arriveF1f2[1]){
arriveF1f2[1](data);
}
return
}
then(f1:IResolve,f2?:IReject){
if(this.status=="fulfilled"){
console.log("运行了成功")
f1(this.data)
}else if(this.status=="rejected"){
f2(this.data)
}else if(this.status=="padding"){
this.f1f2.push([f1,f2])
}
}
}
let test=new Promise2((resolve, reject)=>{
setTimeout(()=>{
reject("成功");
})
})
test.then((data)=>{
console.log("运行了回调1")
console.log(data);
},(data)=>{
console.log("运行了失败回调1");
})
链式调用promise
众所周知promise是可以实现链式调用的,即在.then之后继续.then。
那我们如果要实现这个功能其实也很简单,就是通过then函数返回一个新的promise来实现,然后通过执行resolve/reject函数来实现运行成功回调还是失败回调,具体是下面这几行代码
kotlin
then(f1:IResolve,f2?:IReject){
return new Promise2((resolve, reject)=>{
console.log("运行了then",this.status);
if(this.status=="fulfilled"){
console.log("运行了成功")
this.parse(f1(this.data),resolve,reject)
}else if(this.status=="rejected"){
this.parse(f2(this.data),resolve,reject)
}else if(this.status=="pending"){
this.f1f2.push({
f1:()=>{this.parse(f1(this.data),resolve,reject)},
f2:()=>(this.parse(f2(this.data),resolve,reject))
})
}
})//为了实现链式调用,所以返回promise
}
//冗余代码
parse(result,resolve,reject){
try {
if(result instanceof Promise2){
result.then(resolve,reject)
}else{
resolve(result);
}
}catch (e){
reject(result)
}
}
完整代码如下
kotlin
type IFn=(resolve:IResolve,reject:IReject)=>void
type IResolve<T=any>=(data:T)=>any
type IReject<T=any>=(data:T)=>any
class Promise2{
private status='pending'
private f1f2:any[]=[]
private data:any
constructor(fn:IFn) {
fn.call(this,this.resolve.bind(this),this.reject.bind(this));
}
resolve(data):IResolve{
// console.log(this);
console.log("运行resolve")
this.status="fulfilled"
this.data=data;
let arriveF1f2=this.f1f2.shift();
if(arriveF1f2?.f1){
arriveF1f2.f1();
}
return
}
reject(data):IReject{
this.status="rejected"
this.data=data;
let arriveF1f2=this.f1f2.shift();
if(arriveF1f2.f2){
arriveF1f2.f2();
}
return
}
then(f1:IResolve,f2?:IReject){
return new Promise2((resolve, reject)=>{
console.log("运行了then",this.status);
if(this.status=="fulfilled"){
console.log("运行了成功")
this.parse(f1(this.data),resolve,reject)
}else if(this.status=="rejected"){
this.parse(f2(this.data),resolve,reject)
}else if(this.status=="pending"){
this.f1f2.push({
f1:()=>{this.parse(f1(this.data),resolve,reject)},
f2:()=>(this.parse(f2(this.data),resolve,reject))
})
}
})//为了实现链式调用,所以返回promise
}
//冗余代码
parse(result,resolve,reject){
try {
if(result instanceof Promise2){
result.then(resolve,reject)
}else{
resolve(result);
}
}catch (e){
reject(result)
}
}
}
let test=new Promise2((resolve, reject)=>{
setTimeout(()=>{
resolve("成功");
})
})
test.then((data)=>{
console.log("运行了回调1")
console.log(data);
},(data)=>{
console.log("运行了失败回调1");
}).then((data)=>{
console.log("运行了回调2")
console.log(data);
},(data)=>{
console.log("运行了失败回调2");
})
这大概就是实现promise的完整过程啦!写完这个过程还是很考验js基础的我感觉!
好啦我是发面糕!我们下回再见!(●'◡'●)