Promise.all/allSettled和Promise.race/any方法的使用场景

290 阅读4分钟

Promise.all方法使用场景

用户点击按钮,会向服务器请求数据,但是数据分别是不同的后端接口获取的数据,需要展示在一个span中
如果用户还没请求完成 则span会显示加载中...
等待两个后端接口都返回了数据 才会将数据展示在span 中
   <div class="content">
      <button class="btn">开始请求多组数据</button>
      <span class="text"></span>
   </div>


    const btn = document.querySelector('.btn');
    const text = document.querySelector('.text');

    // 请求数据的接口1
    function getData1() {
      return new Promise((resolve, reject) => {
        setTimeout(() => {
          return resolve('getData1')
        }, 1000)
      })
    }

    // 请求数据的接口2
    function getData2() {
      return new Promise((resolve, reject) => {
        setTimeout(() => {
          return resolve('getData2')
        }, 3000)
      })
    }

    btn.addEventListener('click', async () => {
      // 如果用户还没请求完成 则span会显示加载中
      text.innerHTML = '加载中.....'

      try {
        const res1 = await getData1();
        const res2 = await getData2();
        Promise.all([res1, res2])
          // 如果向后端请求的数据 全部请求成功 则把数据渲染到页面上
          .then(res => {
            let strHtml = '';
            res.forEach(item => strHtml += item)
            text.innerHTML = strHtml;
          })
      } catch (err) {
        // 如果reject被执行 会到 catch里面来 .. 可以执行相应的代码
        text.innerHTML = `某个接口请求失败,数据无法渲染`;
        console.log('某一个后端接口请求失败', err)
      }
    })

1.gif

Promise.allSettled方法使用场景

根据上面的案例,假设用户部分数据没有请求成功,但是还是要把请求成功的数据展示在页面上,all方法就有些无能为力了。
  <div class="content">
    <button class="btn">开始请求多组数据</button>
    <span class="text"></span>
  </div>

    const btn = document.querySelector('.btn');
    const text = document.querySelector('.text');

    // 请求数据的接口1
    function getData1() {
      return new Promise((resolve, reject) => {
        setTimeout(() => {
          return resolve('getData1')
        }, 1000)
      })
    }

    // 请求数据的接口2
    function getData2() {
      return new Promise((resolve, reject) => {
        setTimeout(() => {
          return reject('getData2')
        }, 3000)
      })
    }

    btn.addEventListener('click', async () => {
      // 如果用户还没请求完成 则span会显示加载中
      text.innerHTML = '加载中.....'
      let res1 = null;
      let res2 = null;
      try {
        res1 = await getData1();
        res2 = await getData2();
      } catch (err) {
        // 打印下 请求失败的数据
        console.log("reject:",err)
      } finally {
        // 现在的需求: 把部分成功的数据展示在页面上
        Promise.allSettled([res1, res2])
          .then(res => {
            let strHtml = ``
            res.forEach(item => {
              if (item.value) {
                strHtml += item.value
              }
            })
            text.innerHTML = strHtml
          })
      }
    })

1.gif

Promise.race方法使用场景

如果有一个Promise有了结果,我们就希望决定最终新Promise的状态,那么可以使用race方法

假设现在有100个接口都是相同的功能,但是要取最快的那一个,将数据展示在页面上。
    const btn = document.querySelector('.btn');
    const text = document.querySelector('.text');

    // 请求数据的接口1
    function getData1() {
      return new Promise((resolve, reject) => {
        setTimeout(() => {
          return reject('getData1')
        }, 400)
      })
    }

    // 请求数据的接口2
    function getData2() {
      return new Promise((resolve, reject) => {
        setTimeout(() => {
          return resolve('getData2')
        }, 401)
      })
    }

    btn.addEventListener('click', async () => {
      // 如果用户还没请求完成 则span会显示加载中
      text.innerHTML = '加载中.....'

      try {
        const res1 = await getData1();
        const res2 = await getData2();
        Promise.race([res1, res2])
          // 如果向后端请求的数据 等全部执行完毕 取最快的那一个
          // 如果最快的那个报错了 那就GG了
          .then(res => {
            text.innerHTML = res
          })
      } catch (err) {
        // 如果reject被执行 会到 catch里面来 .. 可以执行相应的代码
        text.innerHTML = `接口请求失败,数据无法渲染`;
      }
    })

1.gif

Promise.any方法使用场景

和race方法是类似的,any方法会等到一个fulfilled状态,才会决定新Promise的状态,如果所有的Promise都是reject的,那么会报一个AggregateError的错误。

上面的案例我们可以看到如果最快的接口执行的回调是reject,就没办法拿到第二名接口的resolve,这里我们可以使用any。

如果第一个接口报错那么会依次按照速度来取一个不报错的,如果全部报错,那就没办法了。
   <div class="content">
      <button class="btn">开始请求多组数据</button>
      <span class="text"></span>
   </div>

    const btn = document.querySelector('.btn');
    const text = document.querySelector('.text');

    // 请求数据的接口1
    function getData1() {
      return new Promise((resolve, reject) => {
        setTimeout(() => {
          return reject('getData11111111111')
        }, 400)
      })
    }

    // 请求数据的接口2
    function getData2() {
      return new Promise((resolve, reject) => {
        setTimeout(() => {
          return resolve('getData22222')
        }, 401)
      })
    }

    btn.addEventListener('click', async () => {
      // 如果用户还没请求完成 则span会显示加载中
      text.innerHTML = '加载中.....'
      try {
        // any方法会等到一个fulfilled状态,才会决定新Promise的状态
        await Promise.any([getData1(), getData2()]).then(res => {
          text.innerHTML = res
        })
      } catch (e) {
        // 如果100个接口都报错 
        console.log("AggregateError", e, e.errors)
      }
    })

1.gif

总结

  • Promise.all
    • 接收的是数组,得到的结果也是数组,并且一一对应,也可以理解为Promise.all照顾跑的最慢的,最慢的跑完才结束。
    • 只有当所有 Promise 实例都变成 fulfilled 状态,新 Promise 的状态才是 fulfilled 状态,返回所有 promise 实例的 resolve value 数组。
    • 如果有一个 Promise 实例状态是 rejected 状态,则新 Promise 的状态是 rejected,返回第一个 promise reject 的 reason。
    • 这样我们可以取到不同接口的所有数据。
  • Promise.allSettled
    • 返回所有 Promise 实例执行结果数组。不管状态是fulfilled,还是rejected都会返回成一个数组。
    • 这样我们可以取到部分状态为fulfilled的做一些操作。
  • Promise.race
    • 接收的也是数组,不过,得到的却是数组中跑的最快的那个,当最快的一跑完就立马结束。不论是fulfilled 或 rejected 状态。
    • 这样如果跑的最快的跑错了那么就GG了。
  • Promise.any
    • 返回数组中跑的最快并且状态为fulfilled的那个,如果数组状态全部为rejected,则报AggregateError错误。
    • 这样会从数组中挑最快的并且不报错的,如果全部报错,那就没办法了。