手撕promise
promise有三种状态
创建初始结构
// 初始结构
class Commitment {
static PENDING='待定';static FULFILLED='成功';static REJECT='拒绝';
constructor(func){
// status状态
this.status = Commitment.PENDING
// result对应状态的结果
this.result = null
func(this.resolve,this.reject)
}
// 因为resolve和reject也是方法
resolve(result){
if(this.status === Commitment.PENDING){
this.status = Commitment.FULFILLED
this.result = result
}
}
reject(result){
if(this.status === Commitment.PENDING){
this.status = Commitment.REJECT
this.result = result
}
}
}
let commitment = new Commitment(resolve=>{
resolve('123')
})
写到这里直接new一个Commitment实例,结果会报错
为什么找不到status呢,因为constructor里面的this指向的是promise实例,而constructor外面指向的是其构造函数的原型对象
因此要修改this指向
修改this指向
class Commitment {
static PENDING='待定';static FULFILLED='成功';static REJECT='拒绝';
constructor(func){
// status状态
this.status = Commitment.PENDING
// result对应状态的结果
this.result = null
// 在这里为resolve和reject函数修改this指向
func(this.resolve.bind(this),this.reject.bind(this))
}
接下来实现.then
class Commitment {
then(onFULFILLED,onREJECT){
if(this.status === Commitment.FULFILLED){
onFULFILLED(this.result)
}
if(this.status === Commitment.REJECT){
onREJECT(this.result)
}
}
}
解决异常
异常一
// 原生promise
let promise = new Promise((resolve,reject)=>{
throw new Error('promise错误')
})
promise.then(
result=>{console.log(result)},
result=>{console.log(result.message)}// promise错误
)
在原生promise中,如果promise自己报错,then的第二个函数能接收到,因此我们要用try…catch捕获
class Commitment {
static PENDING='待定';static FULFILLED='成功';static REJECT='拒绝';
constructor(func){
// status状态
this.status = Commitment.PENDING
// result对应状态的结果
this.result = null
try{
func(this.resolve.bind(this),this.reject.bind(this))
} catch(error){
this.reject(error)
}
}
}
异常二
// 原生promise
let promise = new Promise((resolve,reject)=>{
resolve('成功')
reject('失败')
})
promise.then(
undefined,
result=>{console.log(result.message)}
)
// 不会报错
在原生promise中,.then里的两个参数如果不为函数,也不会报错;但是我们的手写代码会报错,因此要加异步判断
class Commitment {
then(onFULFILLED,onREJECT){
// 如果参数是一个函数,就把自己赋值给自己(不变),如果不是函数,则赋一个空函数
onFULFILLED = typeof onFULFILLED === 'function' ? onFULFILLED : ()=>{}
onREJECT = typeof onREJECT === 'function' ? onREJECT : ()=>{}
if(this.status === Commitment.FULFILLED){
onFULFILLED(this.result)
}
if(this.status === Commitment.REJECT){
onREJECT(this.result)
}
}
}
实现异步
要实现异步,先套上setTimeout
then(onFULFILLED,onREJECT){
// 如果参数是一个函数,就把自己赋值给自己(不变),如果不是函数,则赋一个空函数
onFULFILLED = typeof onFULFILLED === 'function' ? onFULFILLED : ()=>{}
onREJECT = typeof onREJECT === 'function' ? onREJECT : ()=>{}
if(this.status === Commitment.FULFILLED){
setTimeout(()=>{
onFULFILLED(this.result)
})
}
if(this.status === Commitment.REJECT){
setTimeout(()=>{
onREJECT(this.result)
})
}
}
之后会有一个问题
在说这个问题前,要先了解什么是宏任务和微任务
宏任务包括:
-
script整体代码
-
异步ajax请求
-
setTimeout、setInterval
-
文件操作
-
其他
微任务包括:
- Promise.then .catch .finally(注意是Promise的这三个方法,Promise实例本身还是属于同步任务的)
- process.nextTick
- 其他
因为script本身就是一个宏任务,所以事件循环本质上是从宏任务开始的,先执行宏任务里的微任务,再执行宏任务,再执行另一个宏任务,依次反复。当然同步任务是最先执行。
// 原生promise
console.log('1')
let promise = new Promise((resolve,reject)=>{
console.log('2')
setTimeout(()=>{
resolve('5')
console.log('4')
})
})
promise.then(
result =>{console.log(result)}
)
console.log('3')
// 输出结果为12345
但是按照刚刚讲的,同步任务执行完输出123之后,应该先执行微任务.then,此时resolve(‘5’)还为执行,所以应该没有输出,之后再执行宏任务setTimeout输出4
结果应为1234
说明了,原生promise对于这里进行了处理
实现方法:
因为执行then之前没有执行resolve,导致then当时的状态为PENDING待定状态,所以我们加一步判断,判断当前状态为待定时,让then稍后再执行,执行完了resolve再执行then
具体的实现为,创建this.resolveCallbacks=[]保存resolve的执行结果
创建this.rejectCallbacks=[]保存reject的执行结果
在then里,判断当前状态为待定时,将对应的函数onFULFILLED,onREJECT都push到对应数组中
之后在resolve和reject里遍历数组
这里还有一个坑,resolve和reject在事件循环末尾执行,所以要在resolve和reject里加上setTimeout
实现代码
class Commitment {
static PENDING='待定';static FULFILLED='成功';static REJECT='拒绝';
constructor(func){
// status状态
this.status = Commitment.PENDING
// result对应状态的结果
this.result = null
// 保存resolve函数
this.resolveCallbacks=[]
// 保存reject函数
this.rejectCallbacks=[]
try{
func(this.resolve.bind(this),this.reject.bind(this))
} catch(error){
this.reject(error)
}
}
// 因为resolve和reject也是方法
resolve(result){
setTimeout(()=>{
if(this.status === Commitment.PENDING){
this.status = Commitment.FULFILLED
this.result = result
this.resolveCallbacks.forEach(
callback=>{
callback(result)
}
)
}
})
}
reject(result){
setTimeout(()=>{
if(this.status === Commitment.PENDING){
this.status = Commitment.REJECT
this.result = result
this.rejectCallbacks.forEach(
callback=>{
callback(result)
}
)
}
})
}
then(onFULFILLED,onREJECT){
// 如果参数是一个函数,就把自己赋值给自己(不变),如果不是函数,则赋一个空函数
onFULFILLED = typeof onFULFILLED === 'function' ? onFULFILLED : ()=>{}
onREJECT = typeof onREJECT === 'function' ? onREJECT : ()=>{}
// 这一步判断PENDING状态是为了实现异步
if(this.status === Commitment.PENDING ){
this.resolveCallbacks.push(onFULFILLED)
this.resolveCallbacks.push(onREJECT)
}
if(this.status === Commitment.FULFILLED){
setTimeout(()=>{
onFULFILLED(this.result)
})
}
if(this.status === Commitment.REJECT){
setTimeout(()=>{
onREJECT(this.result)
})
}
}
}
带大家理一下顺序
let promise = new Promise((resolve,reject)=>{
console.log('2')
setTimeout(()=>{
resolve('5')
console.log('4')
})
})
promise.then(
result =>{console.log(result)}
)
/*
**new一个实例输出2,运行遇到setTimeout异步先不执行,到then方法
**then里面发现现在的状态是待定,就把函数参数push到对应数组
**现在开始执行setTimeout里面的内容,先执行resolve
**发现resolve函数上来就是一个setTimeout异步,只能出来先执行console.log('4'),输出4
**最后执行在消息队列中的resolve函数
*/
实现链式
把then中的功能返回一个新的promise就可以了
then(onFULFILLED,onREJECT){
return new Commitment((resolve,reject)=>{
// 如果参数是一个函数,就把自己赋值给自己(不变),如果不是函数,则赋一个空函数
onFULFILLED = typeof onFULFILLED === 'function' ? onFULFILLED : ()=>{}
onREJECT = typeof onREJECT === 'function' ? onREJECT : ()=>{}
// 这一步判断PENDING状态是为了实现异步
if(this.status === Commitment.PENDING ){
this.resolveCallbacks.push(onFULFILLED)
this.resolveCallbacks.push(onREJECT)
}
if(this.status === Commitment.FULFILLED){
setTimeout(()=>{
onFULFILLED(this.result)
})
}
if(this.status === Commitment.REJECT){
setTimeout(()=>{
onREJECT(this.result)
})
}
})
}
最终代码
class Commitment {
static PENDING='待定';static FULFILLED='成功';static REJECT='拒绝';
constructor(func){
// status状态
this.status = Commitment.PENDING
// result对应状态的结果
this.result = null
// 保存resolve函数
this.resolveCallbacks=[]
// 保存reject函数
this.rejectCallbacks=[]
try{
func(this.resolve.bind(this),this.reject.bind(this))
} catch(error){
this.reject(error)
}
}
// 因为resolve和reject也是方法
resolve(result){
setTimeout(()=>{
if(this.status === Commitment.PENDING){
this.status = Commitment.FULFILLED
this.result = result
this.resolveCallbacks.forEach(
callback=>{
callback(result)
}
)
}
})
}
reject(result){
setTimeout(()=>{
if(this.status === Commitment.PENDING){
this.status = Commitment.REJECT
this.result = result
this.rejectCallbacks.forEach(
callback=>{
callback(result)
}
)
}
})
}
then(onFULFILLED,onREJECT){
return new Commitment((resolve,reject)=>{
// 如果参数是一个函数,就把自己赋值给自己(不变),如果不是函数,则赋一个空函数
onFULFILLED = typeof onFULFILLED === 'function' ? onFULFILLED : ()=>{}
onREJECT = typeof onREJECT === 'function' ? onREJECT : ()=>{}
// 这一步判断PENDING状态是为了实现异步
if(this.status === Commitment.PENDING ){
this.resolveCallbacks.push(onFULFILLED)
this.resolveCallbacks.push(onREJECT)
}
if(this.status === Commitment.FULFILLED){
setTimeout(()=>{
onFULFILLED(this.result)
})
}
if(this.status === Commitment.REJECT){
setTimeout(()=>{
onREJECT(this.result)
})
}
})
}
}