自己实现一个promise是前端常见的面试题之一(虽然我还没遇到过),同时也是深入理解promise的一种途径,通过自己实现,来更加深刻的理解其中的原理
要自己实现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((resolve,reject)=>{
setTimeout(()=>{resolve(1)})
}).then(x=>console.log(x))
- 这是因为在执行then时,resolve还没有执行,status还是PENDING的状态
- 如果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 = []
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:()=>{}
三、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的输入,应该用resolve包裹
- throw的内容是下一个then的onRejected的输入,应该用reject包裹
首先要能够捕获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(() => {
})
}
}
接着考虑异步的情况下,异步的情况下,执行到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 (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) {
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)
}
})
})
}
}
这个版本基本上能和标准的promise一致了,但是还有一些细节问题,比如下面代码的执行结果
new Promise((resolve, reject) => {
resolve(new Promise((rs, rj) => {rs(9)}))
}).then(res => {
console.log(`res:${res}`)
},err => {
console.log(`err:${err}`)
})
//res:9
new Promise((resolve, reject) => {
resolve(new Promise((rs, rj) => {rj(9)}))
}).then(res => {
console.log(`res:${res}`)
},err => {
console.log(`err:${err}`)
})
//err:9
因此下一步就是解决resolve的内容是promise的情况
ps:下面代码效果是一致的,即只有resolve会解析内部promise的内容
new Promise((resolve, reject) => {
reject(new Promise((rs, rj) => {rs(9)}))
}).then(res => {
console.log(`res:${res}`)
},err => {
console.log(`err:${err}`)
})
//err:[object Promise]
new Promise((resolve, reject) => {
reject(new Promise((rs, rj) => {rj(9)}))
}).then(res => {
console.log(`res:${res}`)
},err => {
console.log(`err:${err}`)
})
//err:[object Promise]
同理then中return返回的promise也会解析,throw返回的promise不会解析
new Promise((resolve, reject) => {
resolve(1)
}).then(res => {
return new Promise(resolve=>resolve(res))
}).then(res => {
console.log(`res:${res}`)
},err => {
console.log(`err:${err}`)
})
//res:1
new Promise((resolve, reject) => {
resolve(1)
}).then(res => {
throw new Promise(resolve=>resolve(res))
}).then(res => {
console.log(`res:${res}`)
},err => {
console.log(`err:${err}`)
})
// err:[object Promise]
四、解决resolve的内容是promise的情况
只有resolve和return中的promise会被解析,即只用this.value会被解析,而this.reason则不会
因此只需要修改if (this.status === FULFILLED) {}和that.resolvedCb.push(() => {})中的内容
先考虑同步状态,执行到this.status === FULFILLED时,如果this.value是一个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 === 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(() => {
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) => {
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) => {
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(() => {
try {
resolve(onRejected(this.reason))
} catch (e) {
reject(e)
}
})
})
}
}
效果对比:

五、promise的部分API实现
Promise.resolve
Promise.resolve = function(value){
return new Promise(resolve=>{
resolve(value)
})
}
Promise.reject
Promise.reject = function(reason){
return new Promise((resolve,reject)=>{
reject(reason)
})
}
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)
})
})
})
}
ps:有个bug,如下
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), res => console.log(res))
真正的promise.all返回是[3,2,1,4],我的返回是[1,2,3,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);
}
})
})
})
}
2059

被折叠的 条评论
为什么被折叠?



