要自己实现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)
})
}
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))
代码输出为:

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





