个人主页原文链接
自己实现一个promise是前端常见的面试题之一(虽然我还没遇到过),同时也是深入理解promise的一种途径,通过自己实现,来更加深刻的理解其中的原理
要自己实现promise,首先得了解Promises/A+条例到底有哪些内容~
同步版先不考虑其他的,直接实现能按照 promise --> then 的顺序执行
const PENDING = "pending"
const FULFILLED = "fulfilled"
const REJECTED = "rejected"
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时,需要如下操作:
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)
}
}
其中的关键代码如下:
if(this.status === PENDING){
this.resolvedCb.push(onFulfilled)
this.rejectedCb.push(onRejected)
}
that.resolvedCb.forEach(cb=>cb(that.value))
that.rejectedCb.forEach(cb=>cb(that.reason))
onFulfilled = onFulfilled instanceof Function?onFulfilled:()=>{}
onRejected = onRejected instanceof Function?onRejected:()=>{}
根据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的回调函数的输入
首先要能够捕获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和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.resolve = function(value){
return new Promise(resolve=>{
resolve(value)
})
}
Promise.reject = function(reason){
return new Promise((resolve,reject)=>{
reject(reason)
})
}
Promise.all方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。
const p = Promise.all([p1, p2, p3]);
p的状态由p1、p2、p3决定,分成两种情况。
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 实例,包装成一个新的 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);
}
})
})
})
}