最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 同一个套路手撕 Promise 的 all、allSettled、any、race 方法

    正文概述 掘金(lxxxv5)   2021-04-07   855

    同一个套路手撕 Promise 的 all、allSettled、any、race 方法

    异同点

    先来看看他们的共同点:

    1. 这些方法的参数都接收一个 promise 的 iterable 类型(注:Array,Map,Set 都属于 ES6 的 iterable 类型)的输入。
    2. 这些方法都返回一个 Promise 实例。

    再看看他们的不同点:

    1. 返回的 Promise 实例的状态改变时机不同
      • all 方法在所有输入的 Promise 实例都 resolve 后执行自身的 resolve 回调,在任意一个输入的 Promise 实例 reject 后执行自身的 reject 回调。
      • allSettled 方法在所有输入的 Promise 实例都改变了状态(执行 resolve 回调或 reject 回调)后执行自身的 resolve 回调。
      • any 方法在所有输入的 Promise 实例都 reject 后执行自身的 reject 回调,在任意一个输入的 Promise 实例 resolve 后执行自身的 resolve 回调。
      • race 方法在任意一个输入的 Promise 实例改变状态后以相同的状态改变自身。
    2. 返回的 Promise 实例的终值(eventual)或拒因(reason)不同
      • all 方法返回的 Promise 实例终值是一个数组,数组的成员是所有输入的 Promise 实例的终值,并将会按照参数内的 promise 顺序排列,而不是由调用 promise 的完成顺序决定。拒因是输入的 Promise 实例中第一个状态变为 rejected 的拒因。
      • allSettled 方法返回的 Promise 实例终值也是一个数组,顺序同 promise 输入顺序,其中每个成员在输入 promise 为 resolved 状态时为 {status:'fulfilled', value:同一个终值}
    3. 参数为空迭代对象时,返回值不同
      • all 方法会同步地返回一个已完成(resolved)状态的 promise,其终值是一个空数组。
      • allSettled 方法和 all 方法表现相同。
      • any 方法会同步地返回一个已失败(rejected)状态的 promise,其拒因是一个 AggregateError 对象。
      • race 方法返回一个永远等待的 promise。

    以上是这四个 allallSettledanyrace 方法的横向对比,如果想综合查看某个方法的描述可以翻阅文章末尾的参考资料。

    接下来就是我们的实现环节,但为了简化,我们仅处理参数为数组的情况,其他 iterable 类型(例如 Set、Map、String 等)的参数差异主要在于取成员个数或遍历方式不一样。

    首先处理的是当参数为空迭代对象时,我们的模板长这样:

    +function template(promises){
    +    if(promises.length === 0){
    +       //  根据不同情况作处理
    +    }
    +}
    

    当参数的长度不为空时,返回一个新的 Promise:

    function template(promises){
        if(promises.length === 0){
           //  根据不同情况作处理
        }
    +   return new Promise((resolve,reject) => {
    +       //  根据不同情况处理
    +   })
    }
    

    定义一个结果收集数组和一个表示符合条件的 promise 状态个数变量:

    function template(promises){
        if(promises.length === 0){
           //  根据不同情况作处理
        }
    +   let result = [], num = 0;
        return new Promise((resolve,reject) => {
            //  根据不同情况处理
        })
    }
    

    在返回的 promise 内部遍历参数,为其添加 then 回调,回调中根据不同情况作处理,最后的模板如下:

    function template(promises) {
      if (promises.length === 0) {
        //  根据不同情况作处理
      }
      let result = [],
        num = 0
      return new Promise((resolve, reject) => {
        const check = () => {
          if (num === promises.length) {
            //    根据不同情况调用 resolve 或 reject
          }
        }
        promises.forEach(item => {
          Promise.resolve(item).then(
            res => {
              //  根据不同情况处理 result、num 和调用 resolve、reject、check 方法
            },
            err => {
              //  根据不同情况处理 result、num 和调用 resolve、reject、check 方法
            }
          )
        })
      })
    }
    

    插播一下 Promise.resolve 这个函数:

    因为 promises 的成员里可能混入了一些不是 promise 的值,所以用 Promise.resolve 去解析后就能统一为其添加 then 回调了。

    具体实现

    1. all 方法

      function all(promises) {
        if (promises.length === 0) return Promise.resolve([])
        return new Promise((resolve, reject) => {
          let result = []
          let num = 0
          const check = () => {
            if (num === promises.length) {
              resolve(result)
            }
          }
          promises.forEach((item, index) => {
            Promise.resolve(item).then(
              res => {
                result[index] = res
                num++
                check()
              },
              err => {
                reject(err)
              }
            )
          })
        })
      }
      
    2. allSettled 方法

      function allSettled(promises) {
        if (promises.length === 0) return Promise.resolve([])
        return new Promise((resolve, reject) => {
          let result = []
          let num = 0
          const check = () => {
            if (num === promises.length) resolve(result)
          }
          promises.forEach((item, index) => {
            Promise.resolve(item).then(
              res => {
                result[index] = { status: 'fulfilled', value: res }
                num++
                check()
              },
              err => {
                result[index] = { status: 'rejected', reason: err }
                num++
                check()
              }
            )
          })
        })
      }
      
    3. any 方法

      function any(promises) {
        if (promises.length === 0) {
          reject(new AggregateError('No Promise in Promise.any was resolved'))
        }
        return new Promise((resolve, reject) => {
          let result = []
          let num = 0
          const check = () => {
            if (num === promises.length) {
              reject(new AggregateError('No Promise in Promise.any was resolved'))
            }
          }
          promises.forEach((item, index) => {
            Promise.resolve(item).then(
              res => {
                resolve(res)
              },
              err => {
                result[index] = err
                num++
                check()
              }
            )
          })
        })
      }
      
    4. race 方法,相对于其他三个方法,race 方法实现比模版更简单点

      function race(promises) {
        if (promises.length === 0) return Promise.resolve()
        return new Promise((resolve, reject) => {
          promises.forEach(item => {
            item.then(resolve, reject)
          })
        })
      }
      

    参考资料

    1. MDN Promise.all()
    2. MDN Promise.allSettled()
    3. MDN Promise.any()
    4. MDN Promise.race()
    5. MDN Promise.resolve()

    起源地下载网 » 同一个套路手撕 Promise 的 all、allSettled、any、race 方法

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元