徒手撸一个Promise

本文详细介绍了如何利用setTimeout实现Promise的基本特性,包括状态转换、then/catch回调的异步触发和链式调用,通过实例展示了从初始化到解决链式调用中断的全过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在通读过作者之前对Promise对象介绍的文章后,本篇文章作者会针对Promise的基本特点利用setTimeout的异步流程控制来进行代码仿真。

1.定义对象

根据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){
  _this.promiseState = 'fulfilled'
  _this.promiseValue = value
}
var reject = function(value){
  _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){
    _this.promiseState = 'fulfilled'
    _this.promiseValue = value
  }
  var reject = function(value){
    _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){
  _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}
promise.html:51 123

当前代码效果如下:

function MyPromise(fn){
  //promise的初始状态为pending,可变成fulfilled或rejected其中之一
  this.promiseState = 'pending'
  this.promiseValue = undefined
  var _this = this
  //定义then的回调函数
  this.thenCallback = undefined
​
  var resolve = function(value){
    _this.promiseState = 'fulfilled'
    _this.promiseValue = value
    //异步的执行then函数中注册的回调函数
    setTimeout(function(){
      if(_this.thenCallback){
        _this.thenCallback(value)
      }
    })
  }
  var reject = function(value){
    _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}
promise.html:52 Uncaught TypeError: Cannot read properties of undefined (reading 'then')
    at promise.html:52
(anonymous) @ promise.html:52
promise.html:51 123

针对该情况,我们需要对Promise的流程控制代码做进一步的加强以实现链式调用,并且在链式调用的过程中将每次的结果顺利的向下传递。

MyPromise.prototype.then = function(callback){
  var _this = this
  //实现链式调用并且每个节点的状态是未知的所以每次都需要返回一个新的Proimse对象
  return new MyPromise(function(resolve,reject){
    //then第一次执行时注册回调函数到当前的Promise对象
    _this.thenCallback = function(value){
      //判断上一次返回的是MyPromise类型时需要在回调中进行下一步以保证异步顺序
      if(value instanceof MyPromise){
        value.then(function(res){
          resolve(callback(res))
        })
      }else{
        //如果是普通数据则直接resolve给下一个Promise对象
        resolve(callback(value))
      }
    }
  })
​
}

将then代码修改为如下之后我们将调用代码更改如下

var p = new MyPromise(function(resolve,reject){
  resolve(123)
})
console.log(p)
p.then(function(res){
  console.log(res)
  return new MyPromise(function(resolve,reject){
    setTimeout(function(){
      resolve(111)
    },1000)
  })
}).then(function(res){
  console.log(res)
  return 789
}).then(function(res){
  console.log(res)
})

会惊喜的发现MyPromise对象可以正常的工作了并且还可以实现何时调用resolve何时执行then的操作

MyPromise {promiseState: 'fulfilled', promiseValue: 123, thenCallback: undefined}
promise.html:64 123
promise.html:71 111
promise.html:74 789

当前状态的代码如下

function MyPromise(fn){
  //promise的初始状态为pending,可变成fulfilled或rejected其中之一
  this.promiseState = 'pending'
  this.promiseValue = undefined
  var _this = this
  //定义then的回调函数
  this.thenCallback = undefined
​
  var resolve = function(value){
    _this.promiseState = 'fulfilled'
    _this.promiseValue = value
    //异步的执行then函数中注册的回调函数
    setTimeout(function(){
      if(_this.thenCallback){
        _this.thenCallback(value)
      }
    })
  }
  var reject = function(value){
    _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){
  var _this = this
  //实现链式调用并且每个节点的状态是未知的所以每次都需要返回一个新的Proimse对象
  return new MyPromise(function(resolve,reject){
    //then第一次执行时注册回调函数到当前的Promise对象
    _this.thenCallback = function(value){
      //判断上一次返回的是MyPromise类型时需要在回调中进行下一步以保证异步顺序
      if(value instanceof MyPromise){
        value.then(function(res){
          resolve(callback(res))
        })
      }else{
        //如果是普通数据则直接resolve给下一个Promise对象
        resolve(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)
  return new MyPromise(function(resolve,reject){
    setTimeout(function(){
      resolve(111)
    },1000)
  })
}).then(function(res){
  console.log(res)
  return 789
}).then(function(res){
  console.log(res)
})

实现catch的流程处理

当Promise的对象触发reject操作的时候他的状态会变更为rejected,此时会触发catch函数,并且catch函数触发后流程结束。

首先仿照then的方式在MyPromise对象中定义好初始通知函数

//定义catch的回调函数
this.catchCallback = undefined
var reject = function(value){
  if(_this.promiseState == 'pending'){
    _this.promiseState = 'rejected'
    _this.promiseValue = value
    setTimeout(function(){
      if(_this.catchCallback){
        _this.catchCallback(value)
      }
    })
  }
}

然后在catch函数中做如下处理

MyPromise.prototype.catch = function(callback){
  this.catchCallback = function(value){
    callback(value)
  }
}

调用代码如下

var p = new MyPromise(function(resolve,reject){
  reject(123)
})
console.log(p)
p.catch(function(err){
  console.log(err)
})

当运行此时代码时我们会发现我们的Promise对象在控制台上可以直接触发catch的回调执行并输出对应的结果

MyPromise {promiseState: 'rejected', promiseValue: 123, thenCallback: undefined, catchCallback: undefined}
promise.html:79 123

实现跨对象执行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(value){
  if(_this.promiseState == 'pending'){
    _this.promiseState = 'rejected'
    _this.promiseValue = value
    setTimeout(function(){
      //判断是否存在catch回调
      if(_this.catchCallback){
        _this.catchCallback(value)
      }else if(_this.thenCallback){
        //再次判断是否存在then的回调,并暂时调用then中的回调
        _this.thenCallback(value)
      }
    })
  }
}

该步骤操作完毕之后我们需要将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{
        //判断上一次返回的是MyPromise类型时需要在回调中进行下一步以保证异步顺序
        if(value instanceof MyPromise){
          value.then(function(res){
            resolve(callback(res))
          })
        }else{
          //如果是普通数据则直接resolve给下一个Promise对象
          resolve(callback(value))
        }
      }
    }
  })
}

修改如下之后调用代码改造为

var p = new MyPromise(function(resolve,reject){
  reject(123)
})
console.log(p)
p.then(function(res){
  console.log('then执行')
  console.log(res)
}).then(function(res){
  console.log('then执行')
  console.log(res)
}).catch(function(err){
  console.log('catch执行')
  console.log(err)
})

输出结果为

MyPromise {promiseState: 'rejected', promiseValue: 123, thenCallback: undefined, catchCallback: undefined}
promise.html:91 catch执行
promise.html:92 123

实现链式调用的中断

本文仅介绍通过返回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
  //实现链式调用并且每个节点的状态是未知的所以每次都需要返回一个新的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{
        //判断上一次返回的是MyPromise类型时需要在回调中进行下一步以保证异步顺序
        if(value instanceof MyPromise){
          //当返回对象是MyPromise类型时如果状态是rejected便执行reject中断Promise
          if(value.promiseState == 'rejected'){
            reject(value.promiseValue)
          }else{
            value.then(function(res){
              resolve(callback(res))
            })
          }
​
        }else{
          //如果是普通数据则直接resolve给下一个Promise对象
          resolve(callback(value))
        }
      }
    }
  })
}

根据代码分析处理逻辑,然后查看运行结果:

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的链式调用便中断了。

总结

通过简单的代码片段我们便可以快速的实现一个微型的Promise对象, 实现手写代码封装Promise对象虽然对工作没有太大的帮助,但是如果可以根据分析Promise的特性通过JS原始的异步流程控制方式来仿真成功Promise对象的内部逻辑就代表你的JS编程水平已经脱离了普通业务开发的程序员了,希望本文的思路可以对前端路上进修的同学们提供帮助,最后附上源代码。

源代码

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)
        }
      })
    }
  }
  //定义catch的回调函数
  this.catchCallback = undefined
  var reject = function(value){
    if(_this.promiseState == 'pending'){
      _this.promiseState = 'rejected'
      _this.promiseValue = value
      setTimeout(function(){
        // console.log(_this.catchCallback)
        //判断是否存在catch回调
        if(_this.catchCallback){
          _this.catchCallback(value)
        }else if(_this.thenCallback){
          //再次判断是否存在then的回调,并暂时调用then中的回调
          _this.thenCallback(value)
        }
      })
    }
  }
  if(fn){
    fn(resolve,reject)
  }else{
    throw('Init Error,Please use a function to init MyPromise!')
  }
}
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{
        //判断上一次返回的是MyPromise类型时需要在回调中进行下一步以保证异步顺序
        if(value instanceof MyPromise){
          //当返回对象是MyPromise类型时如果状态是rejected便执行reject中断Promise
          if(value.promiseState == 'rejected'){
            reject(value.promiseValue)
          }else{
            value.then(function(res){
              resolve(callback(res))
            })
          }
​
        }else{
          //如果是普通数据则直接resolve给下一个Promise对象
          resolve(callback(value))
        }
      }
    }
  })
​
}
​
MyPromise.prototype.catch = function(callback){
  this.catchCallback = function(value){
    callback(value)
  }
}
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)
})

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值