手写Promise核心全过程
最近开始学习一些核心原理,以便于更好地对于JavaScript的一些特性进行理解,第一站就是手写Promise,学习完成之后楼主重新对于书写promise的整个过程进行了梳理,当中或许会有些错误,欢迎批评指正o( ̄▽ ̄)ブ
1. 搭建Promise骨架
// 三种状态
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class myPromise {
constructor(func) {
// myPromise实例创建的时候会先执行一遍传入的方法,所以直接执行func()
// 并且需要将resolve函数和reject函数放入,给用户进行调用
// 由于this问题,可选方案有两种
// 一种是使用bind函数,另一种是直接定义这两个函数为箭头函数
func(this.resolve.bind(this), this.reject.bind(this))
}
resolve() {}
reject() {}
}
2. 添加状态转换等字段
- 在Promise中,状态只能由
pending向fulfilled和rejected转换,并且一旦转换就不能逆转
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class myPromise {
// 添加status字段,用来存储当前这个Promise的状态,默认状态为PENDING
status = PENDING
// 成功的结果
result = undefined
// 失败的原因
reason = undefined
constructor(func) {
func(this.resolve.bind(this), this.reject.bind(this))
}
resolve(result) {
// 判断当前状态,如果是待办,那么就将状态更改为成功
if(this.status === PENDING) {
this.status = FULFILLED
// 修改状态为成功之后,需要将成功的结果传入当前这个promise的result字段
this.result = result
}
}
reject(reason) {
// 同上,将状态更改为失败
if(this.status === PENDING) {
this.status = REJECTED
this.reason = reason
}
}
}
3. 加入then方法
promise中的then方法会对于完成和失败两种状态进行不同的处理,执行传入的方法
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class myPromise {
status = PENDING
result = undefined
reason = undefined
constructor(func) {
func(this.resolve.bind(this), this.reject.bind(this))
}
resolve(result) {
if(this.status === PENDING) {
this.status = FULFILLED
this.result = result
}
}
reject(reason) {
if(this.status === PENDING) {
this.status = REJECTED
this.reason = reason
}
}
/**
* @param {Function} onFULFILLED 传入的成功回调
* @param {Function} onREJECTED 传入的失败回调
*/
then(onFULFILLED, onREJECTED) {
// 调用then方法的时候,需要对于当前的状态进行判断,然后执行传入的对应方法
if(this.status === FULFILLED) {
// 成功的时候执行成功的回调
onFULFILLED(this.result)
} else if(this.status === REJECTED) {
// 状态为失败那么就执行失败的回调函数
onREJECTED(this.reason)
}
}
}
4. 处理异步以及多次调用
- 当前已经完成的代码中,如果在外部resolve或reject函数是在异步请求中的,会导致状态没有变更,只有完成异步之后状态才会改变
const p = new myPromise((resolve, reject) => {
setTimeout(() => {
resolve('成功')
}, 1000)
})
p.then((res) => {
console.log(res) // 没有执行到这个函数
})
- 处理方案:对于没有执行的方法,先存储起来,然后等待状态改变之后就可以进行执行
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class myPromise {
status = PENDING
result = undefined
reason = undefined
// 增加两个回调函数执行栈
fulfilledCallbacks = []
rejectedCallbacks = []
constructor(func) {
func(this.resolve.bind(this), this.reject.bind(this))
}
resolve(result) {
if(this.status === PENDING) {
this.status = FULFILLED
this.result = result
// 状态改变之后从执行栈中弹出方法并且执行
while(this.fulfilledCallbacks.length) {
this.fulfilledCallbacks.shift()(this.result)
}
}
}
reject(reason) {
if(this.status === PENDING) {
this.status = REJECTED
this.reason = reason
// 与resolve相同的操作
while(this.rejectedCallbacks.length) {
this.rejectedCallbacks.shift()(this.result)
}
}
}
then(onFULFILLED, onREJECTED) {
if(this.status === FULFILLED) {
onFULFILLED(this.result)
} else if(this.status === REJECTED) {
onREJECTED(this.reason)
} else {
// 第三个状态:pending
// 向执行栈中压入函数
this.fulfilledCallbacks.push(onFULFILLED)
this.rejectedCallbacks.push(onREJECTED)
}
}
}
5. 处理链式调用以及返回类型处理
promise原本的功能支持链式调用,并且原本的promise支持返回数据或者promise类- 处理这一个功能只需要对于返回的数值进行接收然后进行处理即可
- 链式调用则需要返回一个
promise - 由于
promise的构造函数是马上执行的,所以then的函数体放在新创建出来的promise里面即可
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class myPromise {
status = PENDING
result = undefined
reason = undefined
fulfilledCallbacks = []
rejectedCallbacks = []
constructor(func) {
func(this.resolve.bind(this), this.reject.bind(this))
}
resolve(result) {
if(this.status === PENDING) {
this.status = FULFILLED
this.result = result
while(this.fulfilledCallbacks.length) {
this.fulfilledCallbacks.shift()(this.result)
}
}
}
reject(reason) {
if(this.status === PENDING) {
this.status = REJECTED
this.reason = reason
while(this.rejectedCallbacks.length) {
this.rejectedCallbacks.shift()(this.result)
}
}
}
// 处理返回类型函数
resolvePromise(thenRes, resolve, reject) {
if(thenRes instanceof myPromise) {
// return true -> thenRes type is myPromise
// 当thenRes是promise,那么就执行thenRes的then方法,并且将resolve和reject方法传进去
thenRes.then(resolve, reject)
} else {
resolve(thenRes)
}
}
then(onFULFILLED, onREJECTED) {
let promise = new myPromise((resolve, reject) => {
// 构造函数中的传入函数func()会立即执行
if(this.status === FULFILLED) {
const thenRes = onFULFILLED(this.result)
// resolve(thenRes)
this.resolvePromise(thenRes, resolve, reject)
} else if(this.status === REJECTED) {
const thenRes = onREJECTED(this.reason)
this.resolvePromise(thenRes, resolve, reject)
} else {
this.fulfilledCallbacks.push(onFULFILLED)
this.rejectedCallbacks.push(onREJECTED)
}
})
// 返回myPromise,能够进行链式调用
return promise
}
}
6. 处理错误以及加上异步阻塞
- 在原版的
promise中,如果在正常的进程里面抛出错误,那么会将错误打印出来 - 在原版的
promise中,如果有长时间的定时器,那么会阻塞,等待定时器完成再进行后续的操作,并且then方法中的操作应该是异步的 - 在原版的
promise中,返回promise自身是会报错的
const p = new Promise((resolve, reject) => {
resolve('成功')
})
const p1 = p.then((res) => {
console.log(res) // 成功
return p1
})
p1.then((result) => {
console.log(result)
}, (reason) => {
console.log(reason.message) // Chaining cycle detected for promise #<Promise>
})
- 为了达成这个效果,需要在
resolvePromise这个方法中对于返回的thenRes和返回的promise自身进行判断
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class myPromise {
status = PENDING
result = undefined
reason = undefined
fulfilledCallbacks = []
rejectedCallbacks = []
constructor(func) {
// 处理抛出错误
try {
func(this.resolve.bind(this), this.reject.bind(this))
} catch(error) {
this.reject(error)
}
}
resolve(result) {
if(this.status === PENDING) {
this.status = FULFILLED
this.result = result
while(this.fulfilledCallbacks.length) {
this.fulfilledCallbacks.shift()(this.result)
}
}
}
reject(reason) {
if(this.status === PENDING) {
this.status = REJECTED
this.reason = reason
while(this.rejectedCallbacks.length) {
this.rejectedCallbacks.shift()(this.result)
}
}
}
resolvePromise(promise, thenRes, resolve, reject) {
// 判断自身与thenRes是否一致
if(promise === thenRes) {
return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
}
if(thenRes instanceof myPromise) {
thenRes.then(resolve, reject)
} else {
resolve(thenRes)
}
}
then(onFULFILLED, onREJECTED) {
let promise = new myPromise((resolve, reject) => {
if(this.status === FULFILLED) {
setTimeout(() => {
try {
const thenRes = onFULFILLED(this.result)
// this.resolvePromise(thenRes, resolve, reject)
this.resolvePromise(promise, thenRes, resolve, reject)
} catch(error) {
reject(error)
}
})
} else if(this.status === REJECTED) {
setTimeout(() => {
try {
const thenRes = onREJECTED(this.reason)
this.resolvePromise(thenRes, resolve, reject)
} catch(error) {
reject(error)
}
})
} else {
// 添加异步阻塞,将函数处理成异步函数,再压入栈中
this.fulfilledCallbacks.push(() => {
setTimeout(() => {
try {
const thenRes = onFULFILLED(this.result)
// this.resolvePromise(thenRes, resolve, reject)
this.resolvePromise(promise, thenRes, resolve, reject)
} catch(error) {
reject(error)
}
})
})
this.rejectedCallbacks.push(() => {
setTimeout(() => {
try {
const thenRes = onREJECTED(this.reason)
this.resolvePromise(thenRes, resolve, reject)
} catch(error) {
reject(error)
}
})
})
}
})
return promise
}
}
- 到此完成了基本的promise编写,后续需要完成promise的另外的实例方法
7. promise.then的空值传递
- 在原本的promise中,如果then方法中没有指定传入的方法,那么就需要将resolve或者reject的结果向后传递
const p = new Promise((resolve, reject) => {
resolve('成功')
})
p.then()
.then()
.then()
.then((res) => {
console.log(res) // 成功
})
- 处理的方法是,只需要在then方法前对于
onFULFILLED和onREJECTED方法进行判断并且传递即可
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class myPromise {
status = PENDING
result = undefined
reason = undefined
fulfilledCallbacks = []
rejectedCallbacks = []
constructor(func) {
try {
func(this.resolve.bind(this), this.reject.bind(this))
} catch(error) {
this.reject(error)
}
}
resolve(result) {
if(this.status === PENDING) {
this.status = FULFILLED
this.result = result
while(this.fulfilledCallbacks.length) {
this.fulfilledCallbacks.shift()(this.result)
}
}
}
reject(reason) {
if(this.status === PENDING) {
this.status = REJECTED
this.reason = reason
while(this.rejectedCallbacks.length) {
this.rejectedCallbacks.shift()(this.result)
}
}
}
resolvePromise(promise, thenRes, resolve, reject) {
if(promise === thenRes) {
return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
}
if(thenRes instanceof myPromise) {
thenRes.then(resolve, reject)
} else {
resolve(thenRes)
}
}
then(onFULFILLED, onREJECTED) {
// 使用判断语句对于传入的两个参数进行判断
// 如果是空值,那么就往下传递,不是空值,那么就返回给自身
onFULFILLED = onFULFILLED ? onFULFILLED : (result) => result
onREJECTED = onREJECTED ? onREJECTED : (reason) => { throw reason}
let promise = new myPromise((resolve, reject) => {
if(this.status === FULFILLED) {
setTimeout(() => {
try {
const thenRes = onFULFILLED(this.result)
this.resolvePromise(promise, thenRes, resolve, reject)
} catch(error) {
reject(error)
}
})
} else if(this.status === REJECTED) {
setTimeout(() => {
try {
const thenRes = onREJECTED(this.reason)
this.resolvePromise(thenRes, resolve, reject)
} catch(error) {
reject(error)
}
})
} else {
this.fulfilledCallbacks.push(() => {
setTimeout(() => {
try {
const thenRes = onFULFILLED(this.result)
this.resolvePromise(promise, thenRes, resolve, reject)
} catch(error) {
reject(error)
}
})
})
this.rejectedCallbacks.push(() => {
setTimeout(() => {
try {
const thenRes = onREJECTED(this.reason)
this.resolvePromise(thenRes, resolve, reject)
} catch(error) {
reject(error)
}
})
})
}
})
return promise
}
}