JS异步编程2:手写Promise

Posted 沿着路走到底

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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

以上是关于JS异步编程2:手写Promise的主要内容,如果未能解决你的问题,请参考以下文章

深入了解promise,手写实现promise

JS之手写Promise

Promise

4.2 前端开发日报——JS异步编程之Promise详解和使用总结

JS异步编程,回调函数与promise

JS异步编程,回调函数与promise