循环时是否会并行执行 await 调用?

Will await calls be performed in parallel when looped over?

假设我们看起来像这样:

documents.forEach(url=>{
     await fetch(url).
     then(
        document=>
        console.log(document);
}

这是并行加载所有文档,还是串行加载文档?

换句话说,我们可以将 fetch promise 推入一个数组,然后在该数组上调用 Promise.all,这将并行执行所有 promise。

IIUC 除了 Promise.all 会在 fetch 请求的第一次失败时失败之外,实际上没有区别。

如果回调函数声明为async,则可以使用await

documents.forEach(async url => {
     await fetch(url).then(console.log);
}

执行顺序如下:

  1. 执行forEach方法
  2. 这包括为每个 url 调用回调,一个接一个
  3. 回调的一次执行将调用 fetch,它会立即启动 HTTP 请求和 returns 并承诺。在后台,非 JavaScript、较低级别 API 轮询 HTTP 响应进入。只有后台部分与此处描述的步骤并行发生。
  4. 执行then方法并注册传递给它的回调。 then方法立即returns一个promise
  5. await 开始:回调函数的执行上下文被保存并退出,返回此代码忽略的承诺。
  6. 下一次迭代发生,从步骤 3 开始重复。
  7. forEach方法结束。在这个阶段有几个非JavaScript 线程轮询 HTTP 响应,并且有几个 JS 执行上下文等待稍后执行。
  8. 以某种不可预知的顺序(取决于给出响应的服务器的响应时间),fetch API 解析它已返回的承诺,并在 Promise Job 中放入一条消息队列说明。
  9. JavaScript 事件循环检测到事件并继续执行在步骤 4 中注册的回调(then 回调),输出到控制台。 then 方法返回的承诺已解决,这会将新消息放入承诺作业队列中。
  10. 从队列中拉取消息,这将恢复 forEach 回调的相应执行上下文。在 await 之后继续执行,并且由于没有更多要执行的内容,所以在步骤 5 中返回的承诺已解决(但没有人听)
  11. JavaScript 监视事件队列以进行更多此类工作,并将在某个时候重复第 8 步。

这里没有JavaScript代码与JavaScript并行执行,但是fetchAPI依赖非JavaScript代码,达到"down" 到操作系统函数中,do 运行 与 JavaScript 代码(和其他 OS 函数)并行。

另请注意,代码与此非async/await 变体非常相似:

documents.forEach(url =>
     fetch(url).then(console.log)
);

...因为这个回调 也是 returns 一个承诺。除了 "saving execution context" 部分没有发生在这里,执行计划非常相似。