《JavaScript 权威指南》解读——async/await

《JavaScript 权威指南》解读——async/await

ES2017 引入了 async 和 await 关键字,它们让异步代码可以具有同步代码的观感,和单纯使用 Promise 相比,进一步减少了回调函数的使用。async/await 语法和 Promise 是协同工作的,具体如下:

  • async 用于标记函数,被标记的函数就变为异步函数,本文称 async 函数。
  • async 函数的返回值一定是 Promise,哪怕函数体中全是同步的代码。返回的 Promise 被 fulfill 为 async 函数体的返回值。async 函数体中出现未捕获的异常将导致返回的 Promise 被 reject,原因值即是该异常值。
  • async 函数体中可以没有 await,但 await 只能出现在 async 函数体中。现实中经常单纯为了使用 await 而给函数加 async 标记,此时我们并不关心 async 函数返回的 Promise。
  • await 的操作数一般是 Promise 对象,如果是普通 JS 值,则会隐式转为即刻 fulfill 为该值的 Promise 对象。await 表达式的值是作用的 Promise 对象的 fulfill 值。await 并不阻塞 JS 的执行,但它使得其后的代码一定是在 Promise 决断后才开始执行。如果 await 作用的 Promise 对象 reject 了,则 await 表达式相当于 throw,异常值为 reject 的原因值。

async/await 使得异步过程“并行执行、尽快地串行处理”变得很方便。Promise.all() 可以获得多个独立执行且正常结束的 Promise 的 fulfill 值,这些值的排列顺序是指定的,但这些值的到来时机却是自由的,而且必须等到所有 Promise 都 fufill 后才能获得。Promise 链(即 then 链)可以让异步过程在前一个完成后再发起,从而确保各个 fulfill 值的到来是按顺序的。如果我们希望独立地发起多个异步过程,即让它们存在并行执行的可能,同时又希望按一定顺序尽快地处理它们的 fulfill 值,而不必等到所有 Promise 都 fulfill 后再挨个处理,以上两种做法都无法简单地满足。借助 for 循环和 async/await 语法可以方便地做到:

for(const promise of promises) {
    response = await promise;
    handle(response);
}

以上 promises 是由多个独立的 Promise 对象组成的数组,它们的决断时机是互不相关的,但由于 await,对它们 fulfill 值的处理却是按元素顺序的,同时,并没有等到所有 Promise 都 fulfill 后才开始处理。用一句话概括就是:多个 promise 的异步过程的执行彼此无等待,但对 fulfill 值的处理有先后,且以最快的方式完成对 fulfill 值的处理。

编辑于 2021-03-08 10:07