从一个休眠函数了解promise、async、await(上)

406 阅读5分钟
promise

说起 promise ,很多小伙伴的第一反应就是异步操作,解决回调地狱,任务队列和事件循环,有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败) ,一旦状态改变,就不会再变.....

这些点平常大家看面试题都已经快背下来了,这次我们搞点 promise 的实践玩法

开发的时候,我们经常使用到 promise ,是这样用的

function prom() {
    return new Promise((resolve, reject) => {
        // resolve(222);
        reject(333)
    });
}

function fn() {
    prom().then(res => {
        console.log('res', res); // 222
    }).catch(err => {
        console.log('err', err); //333
    })
}

fn()

这样就可以在 .then 或者是.catch里面去做一些事情

但是有时候,事情往往不是那么简单的,假如遇到需要等待一段时间后再去执行后面的代码,这个时候单单使用 setTimeout 定时器已经不好使了,因为定时器是异步的,我们需要把异步转化为同步去执行,这个时候就可以使用 promise ,但是仅仅一个 promise 也不好搞,这个时候就可以配合 await 搞一个休眠函数来实现

function sleep(time) {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(222);
        }, time);
    });
}

async function fn() {
    console.log('111', 111);
    let res = await sleep(100);
    console.log('res', res);
    console.log('有了res之后我才执行的');
}
fn()

如你所见,没错,就是不用 .then 就获取到了 resolve 的返回值,只需要一个 await

asyncawait 可以直接返回 promise 的结果!!!!! 不用 .then() 去获取

但是 await 只能拿到 resolve 状态,因此对于错误是无能无力的 image.png

这个时候,就该 try...catch... 上场了,它可以捕获其代码块内部的任何错误,因此可以 catchpromisereject 状态。

function sleep(time) {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            reject(222);
        }, time);
    });
}

async function fn() {
    console.log('111', 111);
    let res = null
    try {
        res = await sleep(100);
    } catch (err) {
        res = err
    }
    console.log('res', res);
    console.log('有了res之后我才执行的');
}
fn()
再来看看平常用的少但是很好用的 api
  • finally :不管 Promise 对象最后状态是成功还是失败,都会执行

     new Promise((resolve, reject) => {
         setTimeout(() => {
             resolve(111)
             // reject(222)
         });
     }).then(res => {
         console.log('res', res);
     }).catch(err => {
         console.log('err', err);
     }).finally(() => {
         console.log('我执行了');
     })
    

    注意: finally 方法的回调函数不接受任何参数,这就意味着不知道 Promise 状态到底是fulfilled 还是 rejected

  • all :可以将多个 Promise 实例,包装成一个新的 Promise 实例,参数是一个可迭代对象(一般都是一个数组),返回值是只返回一个 Promise 实例

     var p1 = Promise.resolve(1);
     var p2 = 2;
     var p3 = new Promise((resolve, reject) => {
         setTimeout(resolve, 100, 3);
     });
    
     Promise.all([p1, p2, p3]).then(res => {
         console.log(res); // [1, 2, 3]  // 返回值是一个数组
     });
    

    注意: 只有在传入的数组中的值全部为成功才会返回成功,里面只要有任意一个失败了, 那 Promise.all 将立即变为失败,而且返回值的顺序和传入参数的顺序是一致的!!它不管参数消耗的时间长那个短,一视同仁,它只看顺序!!但是整体返回值消耗的时间是有那个最长的时间决定的。这就带来了一个绝大的好处:在前端开发请求数据的过程中,偶尔会遇到发送多个请求并根据请求顺序获取和使用数据的场景,使用Promise.all 就是最好的选择!

    // 成功
      var p1 = new Promise((resolve, reject) => {
          setTimeout(resolve, 100, 1);
      });
      var p2 = new Promise((resolve, reject) => {
          setTimeout(resolve, 300, 2);
      });
      var p3 = new Promise((resolve, reject) => {
          setTimeout(resolve, 600, 3);
      });
      var p4 = new Promise((resolve, reject) => {
          setTimeout(resolve, 200, 4);
      });
      Promise.all([p1, p2, p3, p4]).then(res => {
          console.log(res); // [1, 2, 3, 4]  // 返回值是一个数组
      }).catch(err => {
          console.log('err', err);
      })
    
      // 失败
      var p1 = new Promise((resolve, reject) => {
      setTimeout(resolve, 100, 1);
      });
      var p2 = new Promise((resolve, reject) => {
          setTimeout(resolve, 200, 2);
      });
      var p3 = new Promise((resolve, reject) => {
          setTimeout(resolve, 300, 3);
      });
      var p4 = new Promise((resolve, reject) => {
          setTimeout(resolve, 400, 4);
      });
      var p5 = new Promise((resolve, reject) => {
          reject('reject');
      });
      Promise.all([p1, p2, p3, p4, p5]).then(res => {
          console.log(res);
      }).catch(err => {
          console.log('err', err); // reject  返回值不是一个数组
      })
    
  • allSettled :以 promise 组成的可迭代对象作为输入,并且返回一个 Promise 实例(数组)。 这个方法和 all 有点类似,但是不同点在于它要等到 所有这些参数实例都返回结果 ,不管是 fulfilled 还是 rejected ,才会返回一个 promsie 结果的对象数组。它的参数是一个 promise 组成的可迭代(例如 Array)对象。

    const promise1 = Promise.resolve(11);
    const promise2 = new Promise((resolve, reject) => setTimeout(reject, 100, 22));
    const promises = [promise1, promise2];
    
    Promise.allSettled(promises).
        then(results =>
            results.forEach((result) => console.log(result.status))
        );
    // [
    //   { status: 'fulfilled', value: 11 },
    //   { status: 'rejected', value: 22 },
    // ]
    
  • any :它的参数和返回值和 all 差不多,区别在功能不一样,any 只要有一个参数的状态改变了,就会立即返回,而且不会返回数组,而且 any 更注重成功,只要有一个执行通过,则认为成功,如果全部拒绝,则认为失败

    var p1 = new Promise((resolve, reject) => {
        setTimeout(resolve, 100, 1);
    });
    var p2 = new Promise((resolve, reject) => {
        resolve(2)
    });
    var p3 = new Promise((resolve, reject) => {
        setTimeout(reject, 300, 3);
    });
    var p4 = new Promise((resolve, reject) => {
        reject(4)
    });
    
    Promise.any([p1, p2, p3, p4]).then(res => {
        console.log('res', res); // 2
    }).catch(err => {
        console.log('err', err); 
    })
    
    Promise.any([p1, p2]).then(res => {
        console.log('res', res); // 2
    }).catch(err => {
        console.log('err', err); 
    })
    
    // 注意了:这儿开始就体现出了它更注重成功
    Promise.any([p1, p3]).then(res => {
        console.log('res', res); // 1  
    }).catch(err => {
        console.log('err', err); 
    })
    
    Promise.any([p1, p4]).then(res => {
        console.log('res', res); // 1   只要有成功,就会返回成功!!
    }).catch(err => {
        console.log('err', err); 
    })
    

    注意: 如果全部都是 rejectedPromise.any() 会抛出一个错误,它还不是一个一般的错误,而是一个 AggregateError

    Promise.any([p3, p4]).then(res => {
        console.log('res', res); 
    }).catch(err => {
        console.log('err', err);  // err AggregateError: All promises were rejected
    })
    
    
  • race :这个方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例,参数也是一个可迭代对象,返回值最先改变的那一个,顾名思义 race 就是赛跑,谁状态改变的快,跑得快,就先返回谁。而且不会返回数组

    var p1 = new Promise((resolve, reject) => {
        setTimeout(resolve, 100, 1);
    });
    var p2 = new Promise((resolve, reject) => {
        resolve(2)
    });
    var p3 = new Promise((resolve, reject) => {
        setTimeout(reject, 300, 3);
    });
    var p4 = new Promise((resolve, reject) => {
        reject(4)
    });
    
    Promise.race([p1, p2, p3, p4]).then(res => {
        console.log('res', res); // 2
    }).catch(err => {
        console.log('err', err);
    })
    
    Promise.race([p1, p2]).then(res => {
        console.log('res', res); // 2
    }).catch(err => {
        console.log('err', err);
    })
    
    Promise.race([p1, p4]).then(res => {
        console.log('res', res); // 4
    }).catch(err => {
        console.log('err', err);
    })
    
    Promise.race([p2, p3]).then(res => {
        console.log('res', res); // 2
    }).catch(err => {
        console.log('err', err);
    })
    

这一套 api 组合拳下来,基本满足了绝大部分的开发需求,什么一次请求多个接口,然后要这样操作那样操作这种情况再也不怕了,批量上传图片要做各种操作也不怕了