从婴儿版到终极版的promise实现

本文详细解析了如何从零开始实现Promise对象,包括同步和异步版本,以及链式调用的实现。深入探讨了在不同状态下的回调处理,解决Promise嵌套问题,并实现了Promise的resolve、reject、all和race等方法。

要自己实现promise,首先得了解Promises/A+条例到底有哪些内容:

  • promise表示异步操作的最终结果。
  • 与promise进行交互的主要方式是通过其then方法,该方法注册回调以接收promise的value或无法履行promise的reason。
  • 该规范详细说明了该then方法的行为。

一、实现一个同步版的promise

同步版先不考虑其他的,直接实现能按照 promise到 then 的顺序执行。

先按照规范,定几个标记量来标记状态:

const PENDING = "pending"
const FULFILLED = "fulfilled"
const REJECTED = "rejected"

promise.then同步执行版本:

function Promise(fn){
    let that = this
    this.status = PENDING
    this.value = undefined
    this.reason = undefined
    
    let resolve = function(value){
        that.value = value
        that.status = FULFILLED
    }
    let reject = function(reason){
        that.reason = reason
        that.status = REJECTED
    }
    
    try{
        fn(resolve,reject)
    }catch(e){
        reject(e)
    }
}

Promise.prototype.then = function(onFulfilled,onRejected){
    if(this.status === FULFILLED){
        onFulfilled(this.value)
    }
    if(this.status === REJECTED){
        onRejected(this.reason)
    }
}

此时运行:

new Promise((res,rej)=>{
	console.log('111')
	res(2222)
}).then(x=>{console.log(x)})

 

二:实现异步版的Promise

在同步版的基础上再解决两个问题:

    1.同步版本在执行异步代码时什么都没有返回

new Promise((res,rej)=>{
	console.log('111')
    setTimeout(()=>{res(222),300})
}).then(x=>console.log(x))

     这是因为在执行then时,resolve还没有执行,status还是PENDING的状态。

     2.如果then中的参数不是Function时的应该有一个默认的回调函数。

 

因此如果执行到then时,需要如下操作:

  • 先判断 onFulfilled与onRejected是否是函数
  • 然后判断status的状态,如果status还是PENDING的话可以先将then中的两个回调函数存起来,待到执行resolve或者reject时再执行。
function Promise(fn){
    let that = this
    this.status = PENDING
    this.value = undefined
    this.reason = undefined
    this.resolvedCb = []               //Cb就是Call-back的缩写,也就是回调函数
    this.rejectedCb = []
    
    let resolve = function(value){
        that.value = value
        that.status = FULFILLED
        that.resolvedCb.forEach(cb=>cb(that.value))
    }
    let reject = function(reason){
        that.reason = reason
        that.status = REJECTED
        that.rejectedCb.forEach(cb=>cb(that.reason))
    }
    
    try{
        fn(resolve,reject)
    }catch(e){
        reject(e)
    }
}

Promise.prototype.then = function(onFulfilled,onRejected){
    onFulfilled = onFulfilled instanceof Function ? onFulfilled : ()=>{}
    onRejected = onRejected instanceof Function ? onRejected : ()=>{}

    if(this.status === FULFILLED){
        onFulfilled(this.value)
    }
    if(this.status === REJECTED){
        onRejected(this.reason)
    }
    if(this.status === PENDING){
        this.resolvedCb.push(onFulfilled)
        this.rejectedCb.push(onRejected)
    }
}

其中的关键代码如下:

     如果状态是PENDING,先保存回调函数:

    if(this.status === PENDING){
        this.resolvedCb.push(onFulfilled)
        this.rejectedCb.push(onRejected)
    }

      异步的情况下resolve或者reject会在then之后执行,因此执行时,回调函数已经放入Cb数组中,可以遍历执行Cb中的回调函数:

 that.resolvedCb.forEach(cb=>cb(that.value))
 that.rejectedCb.forEach(cb=>cb(that.reason))

     如果onFulfilled与onRejected不是函数,应该使用一个默认的函数:

onFulfilled = onFulfilled instanceof Function ? onFulfilled : ()=>{}
onRejected = onRejected instanceof Function ? onRejected : ()=>{}

此时再执行:

new Promise((res,rej)=>{
	console.log('111')
    setTimeout(()=>{res(222),300})
}).then(x=>console.log(x))

得到的结果:

此时一个promise的婴儿版就实现了,不过此时还不能进行链式调用。

 

三、then的链式调用

根据Promises/A+协议,then也应该返回一个promise,同时这也是then链式调用的条件:

       先考虑情况如下:

Promise.prototype.then = function(onFulfilled,onRejected){
    onFulfilled = onFulfilled instanceof Function?onFulfilled:()=>{}
    onRejected = onRejected instanceof Function?onRejected:()=>{}

    if(this.status === FULFILLED){
        onFulfilled(this.value)
        return new Promise(()=>{})
    }
    if(this.status === REJECTED){
        onRejected(this.reason)
        return new Promise(()=>{})
    }
    if(this.status === PENDING){
        this.resolvedCb.push(onFulfilled)
        this.rejectedCb.push(onRejected)
        return new Promise(()=>{})
    }
}

然后FULFILLED与REJECTED状态下,then返回的内容会直接成为下一个then的回调函数的输入:

  • return的内容是下一个then的onFulfilled的输入
  • throw的内容是下一个then的onRejected的输入

首先要能够捕获throw,然后根据结果类型判断使用resolve还是reject

    if (this.status === FULFILLED) {
        return new Promise((resolve, reject) => {
            try {
                let res = onFulfilled(this.value)
                resolve(res)
            } catch (e) {
                reject(e)
            }
        })
    }

      这样就实现了同步版的then链式调用

function Promise(fn) {
    ……
}

Promise.prototype.then = function (onFulfilled, onRejected) {
    onFulfilled = onFulfilled instanceof Function ? onFulfilled : () => {}
    onRejected = onRejected instanceof Function ? onRejected : () => {}

    if (this.status === FULFILLED) {
        return new Promise((resolve, reject) => {
            try {
                resolve(onFulfilled(this.value))
            } catch (e) {
                reject(e)
            }
        })
    }
    if (this.status === REJECTED) {
        return new Promise((resolve, reject) => {
            try {
                resolve(onRejected(this.reason))
            } catch (e) {
                reject(e)
            }
        })
    }
    if (this.status === PENDING) {
        this.resolvedCb.push(onFulfilled)
        this.rejectedCb.push(onRejected)
        return new Promise(() => {

        })
    }
}

    此时输入以下测试用例:

new Promise((res,rej)=>{
	console.log('111')
	res(222)
}).then(res => {
	console.log(`res:${res}`) 
	//return res + 1
    throw res + 2
},err=>{
	console.log(`err:${err}`)
}).then(res2=>{
	console.log(`res2:${res2}`)
},err2=>{
	console.log(`err2:${err2}`)
})

     如果是执行return res +1   则打印出:

 如果是执行throw res+2   则打印出:

     接着考虑异步的情况下,异步的情况下,执行到then时状态还是PENDING,之后的then也是在PENDING状态下返回的promise的基础上调用的。

     考虑异步,没有链式调用时只需要把onFulfilled、onRejected放入回调数组中,链式调用的话,应该把FULFILLED、REJECTED状态的promise中的回调函数放入回调数组中(这里逻辑感觉挺复杂的,主要是保证执行resolvedCb或rejectedCb中内容执行时,有和FULFILLED或REJECTED中promise的回调执行相同的效果

     这里注意一点,onFulfilled和onRejected的输入是新promise的value或者reason属性,因此直接用this,但是整个回调是放在上一个promise的数组中的,因此用that标识原来的this:

Promise.prototype.then = function (onFulfilled, onRejected) {
    onFulfilled = onFulfilled instanceof Function ? onFulfilled : () => {}
    onRejected = onRejected instanceof Function ? onRejected : () => {}
    
    let that = this
    ……
    if (this.status === PENDING) {
        return new Promise((resolve, reject) => {
            that.resolvedCb.push(() => {
                try {
                    resolve(onFulfilled(this.value))
                } catch (e) {
                    reject(e)
                }
            })
            that.rejectedCb.push(() => {
                try {
                    resolve(onRejected(this.reason))
                } catch (e) {
                    reject(e)
                }
            })
        })
    }
}

  到这一步的完整代码如下:

const PENDING = "pending"
const FULFILLED = "fulfilled"
const REJECTED = "rejected"

function Promise(fn) {
    let that = this
    this.status = PENDING
    this.value = undefined
    this.reason = undefined
    this.resolvedCb = []
    this.rejectedCb = []

    let resolve = function (value) {
        that.value = value
        that.status = FULFILLED
        that.resolvedCb.forEach(cb => cb(that.value))
    }
    let reject = function (reason) {
        that.reason = reason
        that.status = REJECTED
        that.rejectedCb.forEach(cb => cb(that.reason))
    }

    try {
        fn(resolve, reject)
    } catch (e) {
        reject(e)
    }
}

Promise.prototype.then = function (onFulfilled, onRejected) {
    onFulfilled = onFulfilled instanceof Function ? onFulfilled : () => {}
    onRejected = onRejected instanceof Function ? onRejected : () => {}

    let that = this

    if (this.status === FULFILLED) {
		return new Promise((resolve, reject) => { 
			try {
				resolve(onFulfilled(this.value))
			} catch (error) {
				reject(error)
			}

		})
    }
    if (this.status === REJECTED) {
		return new Promise((resolve, reject) => {
			try {
				resolve(onRejected(this.reason))
			} catch (e) {
				reject(e)
			}
		})
    }
    if (this.status === PENDING) {
        return new Promise((resolve, reject) => {
            that.resolvedCb.push(() => {
                try {
                    resolve(onFulfilled(this.value))
                } catch (e) {
                    reject(e)
                }
            })
            that.rejectedCb.push(() => {
                try {
                    resolve(onRejected(this.reason))
                } catch (e) {
                    reject(e)
                }
            })
        })
    }
}


此时输入测试:

new Promise((res,rej)=>{
	console.log('111')
	setTimeout(()=>{
		console.log('ininin')
		res(222)
	},2000)
}).then(res=>{
	console.log('then')
	console.log(`res2:${res}`)
	throw res + 2
},err=>{
	console.log(`err2:${err}`)
}).then(res=>{ 
	console.log(`res3:${res}`)
},err=>{
	console.log(`err3:${err}`)
})

输出结果是:

 

这个版本基本上能和标准的promise一致了,但是还有一些细节问题。

比如下面代码的执行结果:(在reslove中返回一个新的promise)

new Promise((resolve, reject) => {
    resolve(new Promise((res, rej) => {res('111')}))
}).then(res => {
    console.log(`res1:${res}`)
},err => {
    console.log(`err1:${err}`)
})


new Promise((resolve, reject) => {
    resolve(new Promise((res, rej) => {rej('222')}))
}).then(res => {
    console.log(`res2:${res}`)
},err => {
    console.log(`err2:${err}`)
})

 

打印出的是:

而下面的代码:(在reject中返回一个新的promise)

new Promise((resolve, reject) => {
    reject(new Promise((res, rej) => {res('333')}))
}).then(res => {
    console.log(`res3:${res}`)
},err => {
    console.log(`err3:${err}`)
})




new Promise((resolve, reject) => {
    reject(new Promise((res, rej) => {rej('444')}))
}).then(res => {
    console.log(`res4:${res}`)
},err => {
    console.log(`err4:${err}`)
})

打印出的是:

还有下面的代码:(在then中返回promise   或者 用throw抛出一个promise)

new Promise((resolve, reject) => {
    resolve('555')
}).then(res => {
    return new Promise(resolve=>resolve(res))
}).then(res => {
    console.log(`res5:${res}`)
},err => {
    console.log(`err5:${err}`)
}) 

new Promise((resolve, reject) => {
    resolve('666')
}).then(res => {
    throw new Promise(resolve=>resolve(res))
}).then(res => {
    console.log(`res6:${res}`)
},err => {
    console.log(`err6:${err}`)
}) 

打印出的是:

可以看出问题:

       后面的值应该是一个字符串,但是却打印出了一个对象  

 

四、解决resolve、reject的内容是promise的情况

       因此要修改if (this.status === FULFILLED) {}和 that.resolvedCb.push(() => {})与if (this.status === REJECTED) {}和 that.rejectedCb.push(() => {})中的内容

       先考虑同步状态,执行到this.status === FULFILLED 或者 this.status === REJECTED时,如果this.value是一个promise或者this.reason是一个promise的话,相当于之后then的链式调用都是接到这个promise后面的,因此:

if (this.status === FULFILLED) {
        return new Promise((resolve, reject) => {
            if (this.value instanceof Promise) {
                this.value.then(onFulfilled, onRejected)
            } else {
                try {
                    resolve(onFulfilled(this.value))
                } catch (e) {
                    reject(e)
                }
            }
        })
    }

if (this.status === REJECTED) {
	return new Promise((resolve, reject) => {
         if (this.reason instanceof Promise) {
            this.reason.then(onFulfilled, onRejected)
         } else {
             try {
				resolve(onRejected(this.reason))
		    } catch (e) {
			    reject(e)
		 }
      }
   }) 
}

再考虑异步的,直接和上面一步同理:

if (this.status === PENDING) {
		return new Promise((resolve, reject) => {
			that.resolvedCb.push(() => {
				if (this.value instanceof Promise) {
					this.value.then(onFulfilled, onRejected)
				} else {
					try {
						resolve(onFulfilled(this.value))
					} catch (e) {
						reject(e)
					}
				}
			})
			
			that.rejectedCb.push(() => {
				if (this.reason instanceof Promise) {
					this.reason.then(onFulfilled, onRejected)
				} else {
					try {
						resolve(onRejected(this.reason))
					} catch (e) {
						reject(e)
					}
				} 
			})
        })
    } 

输入以下测试代码:

new Promise((resolve, reject) => {
    setTimeout(()=>{
		console.log('000')
		resolve(new Promise((res, rej) => {
			res('111')
		}))
	},1000)
}).then(res => {
    console.log(`res1:${res}`)
},err => {
    console.log(`err1:${err}`)
})

输出:

 

因此整个promise的完整代码如下:

const PENDING = "pending"
const FULFILLED = "fulfilled"
const REJECTED = "rejected"

function Promise(fn) {
    let that = this
    this.status = PENDING
    this.value = undefined
    this.reason = undefined
    this.resolvedCb = []
    this.rejectedCb = []

    let resolve = function (value) {
        that.value = value
        that.status = FULFILLED
        that.resolvedCb.forEach(cb => cb(that.value))
    }
    let reject = function (reason) {
        that.reason = reason
        that.status = REJECTED
        that.rejectedCb.forEach(cb => cb(that.reason))
    }

    try {
        fn(resolve, reject)
    } catch (e) {
        reject(e)
    }
}

Promise.prototype.then = function (onFulfilled, onRejected) {
    onFulfilled = onFulfilled instanceof Function ? onFulfilled : () => {}
    onRejected = onRejected instanceof Function ? onRejected : () => {}

    let that = this

	if (this.status === FULFILLED) {
        return new Promise((resolve, reject) => {
            if (this.value instanceof Promise) {
                this.value.then(onFulfilled, onRejected)
            } else {
                try {
                    resolve(onFulfilled(this.value))
                } catch (e) {
                    reject(e)
                }
            }
        })
    }

    if (this.status === REJECTED) {
		return new Promise((resolve, reject) => {
            if (this.reason instanceof Promise) {
                this.reason.then(onFulfilled, onRejected)
            } else {
                try {
					resolve(onRejected(this.reason))
				} catch (e) {
					reject(e)
				}
            }
        }) 
    }
	if (this.status === PENDING) {
		return new Promise((resolve, reject) => {
			that.resolvedCb.push(() => {
				if (this.value instanceof Promise) {
					this.value.then(onFulfilled, onRejected)
				} else {
					try {
						resolve(onFulfilled(this.value))
					} catch (e) {
						reject(e)
					}
				}
			})
			
			that.rejectedCb.push(() => {
				if (this.reason instanceof Promise) {
					this.reason.then(onFulfilled, onRejected)
				} else {
					try {
						resolve(onRejected(this.reason))
					} catch (e) {
						reject(e)
					}
				} 
			})
        })
    } 
}

下面是测试的代码:

new Promise((res,rej)=>{
	console.log('111')
	res(2222)
}).then(x=>{console.log(x)})

new Promise((res,rej)=>{
	console.log('11111')
    setTimeout(()=>{res('setTimeout:2222'),0})
}).then(x=>console.log(x)) 



new Promise((resolve, reject) => {
    resolve(new Promise((res, rej) => {res('111')}))
}).then(res => {
    console.log(`res1:${res}`)
},err => {
    console.log(`err1:${err}`)
})

new Promise((resolve, reject) => {
    resolve(new Promise((res, rej) => {rej('222')}))
}).then(res => {
    console.log(`res2:${res}`)
},err => {
    console.log(`err2:${err}`)
})



new Promise((resolve, reject) => {
    reject(new Promise((res, rej) => {res('333')}))
}).then(res => {
    console.log(`res3:${res}`)
},err => {
    console.log(`err3:${err}`)
})

new Promise((resolve, reject) => {
    reject(new Promise((res, rej) => {rej('444')}))
}).then(res => {
    console.log(`res4:${res}`)
},err => {
    console.log(`err4:${err}`)
})



new Promise((resolve, reject) => {
    resolve('555')
}).then(res => {
    return new Promise(resolve=>resolve(res))
}).then(res => {
    console.log(`res5:${res}`)
},err => {
    console.log(`err5:${err}`)
}) 

new Promise((resolve, reject) => {
    resolve('666')
}).then(res => {
    throw new Promise(resolve=>resolve(res))
}).then(res => {
    console.log(`res6:${res}`)
},err => {
    console.log(`err6:${err}`)
}) 

输出代码如下:

可以看出宏任务、微任务级别都有了。

至此promise的终极版就完成了。下面来完成promise中的某些API:

 

五、promise的部分API实现

1.Promise.resolve

Promise.resolve = function(value){
    return new Promise(resolve=>{
        resolve(value)
    })
}

2.Promise.reject

Promise.reject = function(reason){
    return new Promise((resolve,reject)=>{
        reject(reason)
    })
}

3.Promise.all

Promise.all方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。

const p = Promise.all([p1, p2, p3]);

p的状态由p1、p2、p3决定,分成两种情况。

  • 只有p1、p2、p3的状态都变成fulfilled,p的状态才会变成fulfilled,此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数。
  • 只要p1、p2、p3之中有一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。
Promise.all = function (promises) {
    let resolveList = []
    return new Promise((resolve, reject) => {
        if(promises.length === 0){ //promises为空数组的情况下,会返回resolve([])
            resolve(resolveList)
        }
        promises.forEach(p => {
            Promise.resolve(p).then(re => {
                resolveList.push(re)
                if (promises.length === resolveList.length) {
                    //因为promise异步的原因,还是得放里面
                    resolve(resolveList)
                }
            }, rj => {
                reject(rj)
            })
        })
    })
}

测试代码如下:

var a = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('3')
    },300)
})
var b = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('2')
    },200)
})
var c = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('1')
    },100)
})
var d = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('4')
    },400)
})

Promise.all([a, b, c, d]).then(res => console.log(res), err => console.log(err))

结果为:

如果把测试代码修改为:

var a = new Promise((resolve, reject) => {
    setTimeout(() => {
        reject('3')                  //修改了这里
    },300)
})
var b = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('2')
    },200)
})
var c = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('1')
    },100)
})
var d = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('4')
    },400)
})

Promise.all([a, b, c, d]).then(res => console.log(res), err => console.log(err))

则输出:

 

4.Promise.race

Promise.race方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例。

const p = Promise.race([p1, p2, p3]);

上面代码中,只要p1、p2、p3之中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给p的回调函数。

Promise.race = function (promises) {
    let flag = true
    return new Promise((resolve, reject) => {
        promises.forEach(p => {
            Promise.resolve(p).then(re => {
                if (flag) {
                    flag = false
                    resolve(re);
                }
            }, rj => {
                if (flag) {
                    flag = false
                    reject(rj);
                }
            })
        })
    })
}

测试代码为:

var a = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('3')
    },300)
})
var b = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('2')
    },200)
})
var c = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('1')
    },100)
})

Promise.race([a,b,c]).then(res=>console.log(res),err=>console.log(err))

代码输出为:

 

评论 3
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值