Promise规范虽然不长,但细节也比较多,我挑出几个要点简单说明下:

  1. Promise 本质是一个状态机。每个 promise 只能是 3 种状态中的一种:pending、fulfilled 或 rejected。状态转变只能是 pending -> fulfilled 或者 pending -> rejected。状态转变不可逆。
  2. then 方法可以被同一个 promise 调用多次。
  3. then 方法必须返回一个 promise。规范里没有明确说明返回一个新的 promise 还是复用老的 promise(即 return this),大多数实现都是返回一个新的 promise,而且复用老的 promise 可能改变内部状态,这与规范也是相违背的。

Promise.all

  1. let p1 = getjson(url, {}).then(function(res) {
  2. return res;
  3. });
  4. let p2 = getjson(url, {
  5. 'entname': '',
  6. 'page': 1,
  7. 'pageSize': 10
  8. }).then(function(res) {
  9. return res;
  10. });
  11. Promise.all([p1, p2]).then((p1res) => {
  12. console.log('p1::' + JSON.stringify(p1res[0].responseData));
  13. console.log('p12::' + JSON.stringify(p1res[1].responseData));
  14. });

使用promise返回的promise 在所有promise结束时可以进行调用

应用场景: 在今天,遇到了这样一个问题。在导入时,由于导入信息可能没有地址,也可能没有坐标,需要在导入时将坐标转换为地址或者将地址转换为坐标。但是,这个转换的过程是一个promise的过程(多且杂),目标:在所有转换结束后判断checkResultList是否为空数组,如果是给出通过检查。这里使用的即promise.all操作

另外还有Promise.race用来触发第一个promise结束的事件。

Promise的一些聪明用法

  • 返回首个成功的Promise

从一组Promise里面得到第一个“成功的”结果,同时获得了并发执行的速度和容灾的能力。

Promise.race不满足需求,因为如果有一个Promise率先reject,结果Promise也会立即reject;> Promise.all也不满足需求,因为它会> 等待所有Promise,并且要求所有Promise都成功resolve。

  1. function firstSuccess(promises){
  2. return Promise.all(promises.map(p => {
  3. // If a request fails, count that as a resolution so it will keep
  4. // waiting for other possible successes. If a request succeeds,
  5. // treat it as a rejection so Promise.all immediately bails out.
  6. return p.then(
  7. val => Promise.reject(val),
  8. err => Promise.resolve(err)
  9. );
  10. })).then(
  11. // If '.all' resolved, we've just got an array of errors.
  12. errors => Promise.reject(errors),
  13. // If '.all' rejected, we've got the result we wanted.
  14. val => Promise.resolve(val)
  15. );
  16. }

把resolve的Promise反转成reject,把reject的Promise反转成resolve,然后用Promise.all合并起来。
这样的话,只要有一个原始Promise成功resolve,就会造成Promise.all立刻被reject,实现提前退出!太巧妙了!
这个方法适合的场景:

  • 有多条路可以走,其中任意一条路走通即可,其中有一些路失败也没关系
  • 为了加速得到结果,并发地走多条路,避免瀑布式尝试

异步reduce

有时候业务逻辑要求我们必须串行地处理多个数据,不能像上面那样并发地处理多个数据。即,通过瀑布式的异步操作,将一个数组reduce成一个值。这个时候可以巧妙地使用array.reduce:

(async () => {
    const data = [1, 2, 3]
    const result = await data.reduce(async (accumP, current, index) => {
      // 后面的处理要等待前面完成
      const accum = await accumP;
      const next = await apiCall(accum, current);
      return next
    }, 0);
    console.log(result)  // 6

    async function apiCall(a, b) {
        return new Promise((res)=> {
            setTimeout(()=> {res(a+b);}, 300)
        })
    }
})()