JS异步编程2:手写Promise

1

class MyPromise {
  constructor(fn) {
    // promise 的状态
    this.promiseState = 'pending'

    // promise 的值
    this.promiseResult = undefined

    // 记录then中的callback
    this.thenCallBack = null

    // 记录catch中的callback
    this.catchCallBack = null

    if(fn) {
      fn(this.resolve, this.reject)
    }

    
  }

  static resolve(value) {
    return new MyPromise(function(resolve, reject) {
      resolve(value)
    })
  }

  static reject(errValue) {
    return new MyPromise(function(resolve, reject) {
      reject(errValue)
    })
  }

  static all(promiseArr) {
    const resArr = []
    return new MyPromise(function(resolve, reject) {
      promiseArr.forEach((item, index) => {
        item.then(res => {
          resArr[index] = res
          const allResolved = promiseArr.every(_item => {
            return _item.promiseState === 'fulfilled'
          })

          if (allResolved) {
            resolve(resArr)
          }
        }).catch(err => {
          reject(err)
        })
      })
    })
  }

  static race(promiseArr) {
    let end = false
    return new MyPromise(function(resolve, reject) {
      promiseArr.forEach(item => {
        item.then(function(res) {
          resolve(res)
        }).catch(function(err) {
          if (end === false) {
            end = true
            reject(err)
          }
        })
      })
    })
  }

  resolve = (value) => {
    if (this.promiseState === 'pending') {
      this.promiseState = 'fulfilled'
      this.promiseResult = value

      if (value instanceof MyPromise) {
        value.then((res) => {
          console.log(res)
          if (this.thenCallBack) {
            this.thenCallBack(res)
          }
        })
      } else {
        setTimeout(() => {
          if (this.thenCallBack) {
            this.thenCallBack(value)
          }
        })
      }
    }
  }

  reject = (errValue)  => {
    if (this.promiseState === 'pending') {
      this.promiseState = 'rejected'
      this.promiseResult = errValue

      setTimeout(() => {
        if (this.catchCallBack) {
          this.catchCallBack(errValue)
        } else if (this.thenCallBack) {
          this.thenCallBack(errValue)
        } else {
          throw('catch is not defined')
        }
      })
    }
  }

  then = (callback) => {
    return new MyPromise((resolve, reject) => {
      this.thenCallBack = function(value) {
        if (this.promiseState === 'rejected') {
          reject(value)
        } else {
          const res = callback && callback(value)

          if (res instanceof MyPromise && res.promiseState === 'rejected') {
            res.catch(function(errValue) {
              reject(errValue)
            })
          } else {
            resolve(res)
          }
        }
      }
    })
  }

  catch = (callback) => {
    return new MyPromise((resolve, reject) => {
      this.catchCallBack = function(errValue) {
        const res = callback && callback(errValue)
        resolve(res)
      }
    })
  }
}

Promise 对象的基本结构定义
根据 Promise 对象的特点分析, Promise 存在状态属性和 Promise 的值的属性。初始化 Promise 时需要传⼊⼀个回调函数来进⾏对象的基本设置,回调函数具备两个参数resolve reject ,两个参数均为函数。所以初始化代码如下 :
function MyPromise(fn){
 //promise的初始状态为pending,可变成fulfilled或rejected其中之⼀
 this.promiseState = 'pending'
 this.promiseValue = undefined
 var resolve = function(){
 }
 var reject = function(){
 }

 if(fn){
     fn(resolve,reject)
 }else{
     throw('Init Error,Please use a function to init MyPromise!')
 }
}
根据对象特性,初始化 Promise 时的回调函数是同步执⾏的,所以此时的 fn 直接调⽤即可。
在调⽤ resolve reject 时,需要将 Promise 对象的状态设置为对应的 fulfilled rejected ,其中需要传⼊ Promise 当前的结果,所以此时应该将resolve reject 修改为如下结构。
//保存上下⽂对象
var _this = this
var resolve = function(value){
 if(_this.promiseState == 'pending'){
     _this.promiseState = 'fulfilled'
     _this.promiseValue = value
 } 
}
var reject = function(value){
 if(_this.promiseState == 'pending'){
     _this.promiseState = 'rejected'
     _this.promiseValue = value
 }
}
定义完内部结构之后需要思考 Promise 在状态变更为 fulfilled 以及状态变更为 rejected 时对应的 then catch 会相应 执⾏,所以需要将对象的两个函数初始化:
MyPromise.prototype.then = function(callback){
}
MyPromise.prototype.catch = function(callback){
}
那么初始对象的结构应该整体是这样的:
function MyPromise(fn){
 //promise的初始状态为pending,可变成fulfilled或rejected其中之⼀
 this.promiseState = 'pending'
 this.promiseValue = undefined
 var _this = this

 var resolve = function(value){
     if(_this.promiseState == 'pending'){
         _this.promiseState = 'fulfilled'
         _this.promiseValue = value
     } 
 }

 var reject = function(value){
     if(_this.promiseState == 'pending'){
         _this.promiseState = 'rejected'
         _this.promiseValue = value
     }
 }

 if(fn){
     fn(resolve,reject)
 }else{
     throw('Init Error,Please use a function to init MyPromise!')
 }
}

MyPromise.prototype.then = function(callback){
}

MyPromise.prototype.catch = function(callback){
}
实现 then 的调⽤
在实现了初始结构之后,我们需要使⽤ MyPromise 按照 Promise 的⽅式进⾏编程,来实现他的流程控制部分了。⾸先我们需要让then 跑起来。
定义调⽤代码:
var p = new MyPromise(function(resolve,reject){
 resolve(123)
})

console.log(p) 

p.then(function(res){
 console.log(res)
})
此时执⾏代码时控制台会输出如下内容:
MyPromise
promiseState: "fulfilled"
promiseValue: 123
[[Prototype]]: Object
我们发现我们定义的 Promise 对象状态已经变更但是 then 中的回调函数没有执⾏。
接下来我们实现 then 的触发:
//在MyPromise中改造该部分代码如下
//定义then的回调函数
this.thenCallback = undefined
var resolve = function(value){
  if(_this.promiseState == 'pending'){
     _this.promiseState = 'fulfilled'
     _this.promiseValue = value
     //异步的执⾏then函数中注册的回调函数
     setTimeout(function(){
         if(_this.thenCallback){
         _    this.thenCallback(value)
         }
     })
  }
}


//在then中编写如下代码
MyPromise.prototype.then = function(callback){
 //then第⼀次执⾏时注册回调函数到当前的Promise对象
 this.thenCallback = function(value){
     callback(value)
 }
}
在两处改造完成之后访问⽹⻚会发现控制台上可以输出 then 函数中的回调执⾏的结果并且该结果的参数就是 resolve传⼊的值。
MyPromise {promiseState: 'fulfilled', promiseValue: 123, thenCallback: undefined}
当前代码效果如下:
function MyPromise(fn){
 //promise的初始状态为pending,可变成fulfilled或rejected其中之⼀
 this.promiseState = 'pending'
 this.promiseValue = undefined
 var _this = this
 //定义then的回调函数
 this.thenCallback = undefined
 var resolve = function(value){
     if(_this.promiseState == 'pending'){
         _this.promiseState = 'fulfilled'
         _this.promiseValue = value
         //异步的执⾏then函数中注册的回调函数
         setTimeout(function(){
             if(_this.thenCallback){
                 _this.thenCallback(value)
             }
         })
     }
 }

 var reject = function(value){
     if(_this.promiseState == 'pending'){
         _this.promiseState = 'rejected'
         _this.promiseValue = value
     }
 }

 if(fn){
     fn(resolve,reject)
 }else{
     throw('Init Error,Please use a function to init MyPromise!')
 }
}

MyPromise.prototype.then = function(callback){
 //then第⼀次执⾏时注册回调函数到当前的Promise对象
 this.thenCallback = function(value){
 callback(value)
 }
}

MyPromise.prototype.catch = function(callback){
}

var p = new MyPromise(function(resolve,reject){
 resolve(123)
})

console.log(p) 

p.then(function(res){
 console.log(res)
})
实现 then 的异步链式调⽤
通过上⾯的编程已经可以实现 then ⾃动触发,但是当前我们如果将代码变成如下效果时只有⼀个 then 能执⾏。⽽且控制台会报错
var p = new MyPromise(function(resolve,reject){
 resolve(123)
})

console.log(p) 

p.then(function(res){
 console.log(res)
}).then(function(res){
 console.log(res)
}).then(function(res){
 console.log(res)
})
控制台信息如下:
MyPromise {promiseState: 'fulfilled', promiseValue: 123, thenCallback: undefined}

Uncaught TypeError: Cannot read properties of undefined (reading
'then')
针对该情况,我们需要对 Promise 的流程控制代码做进⼀步的加强以实现链式调⽤,并且在链式调⽤的过程中将每次的结果顺利的向下传递。
//resolve部分代码实现
var resolve = function(value){
 if(_this.promiseState == 'pending'){
     _this.promiseValue = value
     _this.promiseState = 'fulfilled'
     //当传⼊的类型是Promise对象时
     if(value instanceof MyPromise){
         value.then(function(res){
             _this.thenCallback(res)
         })
     }else{
         //当传⼊的数据类型是普通变量时
         setTimeout(function(){
             if(_this.thenCallback){
                 _this.thenCallback(value)
             }
         })
     }
 }
}

//then函数代码实现
MyPromise.prototype.then = function(callback){
 var _this = this
 return new MyPromise(function(resolve,reject){
     _this.thenCallback = function(value){
         var callbackRes = callback(value)
         resolve(callbackRes)
     }
 })
}
then 代码修改为如下之后我们将调⽤代码更改如下
var p = new MyPromise(function(resolve){
 resolve(new MyPromise(function(resolve1){
     resolve1('aaa')
 }))
})

p.then(function(res){
 console.log(res)
 return 123
}).then(function(res){
 console.log(res)
 return new MyPromise(function(resolve){
     setTimeout(function(){
         resolve('Promise')
     },2000)
 })
}).then(function(res){
 console.log(res)
})

console.log(p)
会惊喜的发现 MyPromise 对象可以正常的⼯作了并且还可以实现何时调⽤ resolve 何时执⾏ then 的操作
MyPromise {promiseValue: MyPromise, promiseState: 'fulfilled', catchCallback:
undefined, thenCallback: ƒ}
test.html:57 aaa
test.html:60 123
test.html:67 Promise
当前状态的代码如下
function MyPromise(fn){
 var _this = this
 this.promiseValue = undefined
 this.promiseState = 'pending'
 this.thenCallback = undefined
 this.catchCallback = undefined

 var resolve = function(value){
     if(_this.promiseState == 'pending'){
         _this.promiseValue = value
         _this.promiseState = 'fulfilled'
         if(value instanceof MyPromise){
             value.then(function(res){
                 _this.thenCallback(res)
             })
         }else{
             setTimeout(function(){
                 if(_this.thenCallback){
                     _this.thenCallback(value)
                 }
             })
         }
     }
 }

 var reject = function(err){
 }

 if(fn){
     fn(resolve,reject)
 }else{
     throw('Init Error,Please use a function to init MyPromise!')
 }
}

MyPromise.prototype.then = function(callback){
 var _this = this
 return new MyPromise(function(resolve,reject){
     _this.thenCallback = function(value){
         var callbackRes = callback(value)
         resolve(callbackRes)
     }
 })
}

var p = new MyPromise(function(resolve){
 resolve(new MyPromise(function(resolve1){
     resolve1('aaa')
 }))
})
实现 catch 的流程处理
Promise 的对象触发 reject 操作的时候他的状态会变更为 rejected ,此时会触发 catch 函数,并且 catch 函数触发后流程结束。
⾸先仿照 then 的⽅式在 MyPromise 对象中定义好初始通知函数

//定义catch的回调函数
this.catchCallback = undefined
var reject = function(err){
 if(_this.promiseState == 'pending'){
     _this.promiseValue = err
     _this.promiseState = 'rejected'
     setTimeout(function(){
         if(_this.catchCallback){
             _this.catchCallback(err)
         }
     })
 }
}
然后在 catch 函数中做如下处理
MyPromise.prototype.catch = function(callback){
 var _this = this
 return new MyPromise(function(resolve,reject){
     _this.catchCallback = function(errValue){
         var callbackRes = callback(errValue)
         resolve(callbackRes)
     }
 })
}
调⽤代码如下
var p = new MyPromise(function(resolve,reject){
 reject('err')
})

p.catch(function(err){
 console.log(err)
})
当运⾏此时代码时我们会发现我们的 Promise 对象在控制台上可以直接触发 catch 的回调执⾏并输出对应的结果

MyPromise {promiseValue: 'err', promiseState: 'rejected', thenCallback: undefined,
catchCallback: ƒ}
test.html:73 err
实现跨对象执⾏ catch
在上⾯的案例中已经可以执⾏ MyPromise catch 函数了,但是如果将调⽤代码改为如下⾏为会发现 catch 函数不会执⾏
var p = new MyPromise(function(resolve,reject){
 reject(123)
})

console.log(p) 

p.then(function(res){
 console.log(res)
}).catch(function(err){
 console.log(err)
})
这是因为按照我们编写的代码流程 Promise 对象会⾃动变更状态为 rejected 并且 catch 的回调函数⽆法注册,所以 Promise的流程就断了。这个时候需要追加判断代码让 Promise rejected 时如果没有 catchCallback 再去检测是否存在thenCallback
var reject = function(err){
  if(_this.promiseState == 'pending'){
     _this.promiseValue = err
     _this.promiseState = 'rejected'
     setTimeout(function(){
         if(_this.catchCallback){
             _this.catchCallback(err)
         }else if(_this.thenCallback){
             _this.thenCallback(err)
         }else{
             throw('this Promise was reject,but can not found catch!')
         }
     })
  }
}
该步骤操作完毕之后我们需要将 then 函数中的逻辑再次更改为如下
MyPromise.prototype.then = function(callback){
 var _this = this
 //实现链式调⽤并且每个节点的状态是未知的所以每次都需要返回⼀个新的Proimse对象
 return new MyPromise(function(resolve,reject){
     //then第⼀次执⾏时注册回调函数到当前的Promise对象
     _this.thenCallback = function(value){
         //判断如果进⼊该回调时Promise的状态为rejected那么就直接触发后续Promise的catchCallback
         //直到找到catch
         if(_this.promiseState == 'rejected'){
             reject(value)
         }else{
             var callbackRes = callback(value)
             resolve(callbackRes)
         }
     }
 })
}
修改如下之后调⽤代码改造为
var p = new MyPromise(function(resolve,reject){
 reject('err')
})

p.then(function(res){
 console.log(res)
 return 111
}).then(function(res){
 console.log(res)
 return 111
}).then(function(res){
 console.log(res)
 return 111
}).catch(function(err){
 console.log(err)
})
console.log(p)
输出结果为
MyPromise {promiseValue: 'err', promiseState: 'rejected', catchCallback: undefined,
thenCallback: ƒ}
test.html:91 err
实现链式调⽤的中断
本⽂仅介绍通过返回 Promise 对象来中断链式调⽤,⾸先在 Promise 的原型对象上增加 reject ⽅法如下 :
MyPromise.reject = function(value){
 return new MyPromise(function(resolve,reject){
     reject(value)
 })
}
然后初始化如下调⽤代码
var p = new MyPromise(function(resolve,reject){
 resolve(123)
})

console.log(p) 

p.then(function(res){
 console.log('then1执⾏')
 return 456
}).then(function(res){
 console.log('then2执⾏')
 return MyPromise.reject('中断了')
}).then(function(res){
 console.log('then3执⾏')
 return 789
}).then(function(res){
 console.log('then4执⾏')
 return 666
}).catch(function(err){
 console.log('catch执⾏')
 console.log(err)
})
最后修改调试代码中的 then
MyPromise.prototype.then = function(callback){
 var _this = this
 return new MyPromise(function(resolve,reject){
     _this.thenCallback = function(value){
         if(_this.promiseState == 'rejected'){
             reject(value)
         }else{
             var callbackRes = callback(value)
             if(callbackRes instanceof MyPromise){
                 if(callbackRes.promiseState == 'rejected'){
                     callbackRes.catch(function(errValue){
                         reject(errValue)
                     })
                 }
             }else{
                 resolve(callbackRes)
             }
         }
     }
 })
}
根据代码分析处理逻辑,然后查看运⾏结果:
MyPromise {promiseState: 'fulfilled', promiseValue: 123, thenCallback: undefined,
catchCallback: undefined}
promise.html:100 then1执⾏
promise.html:103 then2执⾏
promise.html:112 catch执⾏
promise.html:113 中断了
最后我们发现在返回 Promise.reject() 之后 then 的链式调⽤便中断了。

1

猜你喜欢

转载自blog.csdn.net/m0_38066007/article/details/122986475#comments_20054078