等待 VS Promise.all

Nic*_*ian 24 javascript node.js promise async-await for-await

这有什么区别吗:

const promises = await Promise.all(items.map(e => somethingAsync(e)));
for (const res of promises) {
  // do some calculations
}
Run Code Online (Sandbox Code Playgroud)

和这个 ?

for await (const res of items.map(e => somethingAsync(e))) {
  // do some calculations
}
Run Code Online (Sandbox Code Playgroud)

我知道在第一个片段中,所有的承诺都被同时触发,但我不确定第二个。for 循环是否等待第一次迭代完成以调用下一个 promise ?还是所有的 Promise 都是同时触发的,循环内部就像是它们的回调?

Ber*_*rgi 19

是的,它们完全不同。for await应该与异步迭代器一起使用,而不是与预先存在的承诺数组一起使用。

只是为了说明,

for await (const res of items.map(e => somethingAsync(e))) …
Run Code Online (Sandbox Code Playgroud)

工作原理相同

const promises = items.map(e => somethingAsync(e));
for await (const res of promises) …
Run Code Online (Sandbox Code Playgroud)

或者

const promises = [somethingAsync(items[0]), somethingAsync(items[1]), …];
for await (const res of promises) …
Run Code Online (Sandbox Code Playgroud)

somethingAsync呼叫将立即发生,一次全部,任何等待之前。然后,它们await一个接一个地被编辑,如果其中任何一个被拒绝,这绝对是一个问题:这将导致未处理的承诺拒绝错误。使用Promise.all是处理 promise 数组的唯一可行选择

for (const res of await Promise.all(promises)) …
Run Code Online (Sandbox Code Playgroud)

请参阅等待多个并发等待操作等待 Promise.all() 和多个等待之间有什么区别?详情。

  • 在我看来,promise 数组被同化为异步迭代器,因为它们将控制权交还给事件循环,对吗?如果在我的 `somethingAsync` 函数中,我正确捕获了所有内容怎么办? (2认同)
  • 是的,数组提供了一个异步迭代器,这就是循环不会抛出协议错误的原因。但是该迭代一次等待一个 Promise,忽略数组中稍后的 Promise 中的错误。当然,如果“somethingAsync”从不出错,就不会发生任何不好的事情,但这很难保证。在一系列 Promise 上使用“for wait”仍然是一个不好的做法。 (2认同)
  • @VigneshM 不,`Promise.all` 和 `for wait` 都不会“执行”任何东西。创建承诺的“somethingAction”调用是预先一次性完成的,无需任何等待,并且操作将同时运行。`for wait` 是错误的,`Promise.all` 是处理这种需要等待多个 Promise 的情况的唯一正确方法。如果您想要顺序执行,则需要执行 `for (const e of items) { const res = wait SomethingAsync(e); ... }`,在循环体中使用 `await`。 (2认同)

Cas*_*ano 10

for await ...当在异步迭代器上当前迭代的计算依赖于一些先前的迭代时,需要出现。如果没有依赖,Promise.all就是你的选择。该for await构造旨在与异步迭代器一起使用,尽管 - 在您的示例中,您可以将它与一系列承诺一起使用。

有关使用无法使用以下方法重写的异步迭代器的示例,请参阅javascript.info一书中的示例分页数据Promise.all

(async () => {
  for await (const commit of fetchCommits('javascript-tutorial/en.javascript.info')) {
    console.log(commit.author.login);
  }
})();
Run Code Online (Sandbox Code Playgroud)

这里fetchCommits异步迭代器向fetchGitHub 存储库的提交发出请求。在fetch与30个提交一个JSON响应,并且还提供了一个链接到下一个页面Link标题。因此下一次迭代只能在上一次迭代有下一个请求的链接后开始

async function* fetchCommits(repo) {
  let url = `https://api.github.com/repos/${repo}/commits`;

  while (url) {
    const response = await fetch(url, { 
      headers: {'User-Agent': 'Our script'}, 
    });

    const body = await response.json(); // (array of commits

    // The URL of the next page is in the headers, extract it using a regexp
    let nextPage = response.headers.get('Link').match(/<(.*?)>; rel="next"/);
    nextPage = nextPage?.[1];

    url = nextPage;

    for(let commit of body) { // yield commits one by one, until the page ends
      yield commit;
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

  • 非常清楚,谢谢。在您的代码示例中,“for wait”的行为类似于“yield*”,不是吗?因为每次迭代都会产生多个项目 (2认同)
  • 没有生成器组合,只有每次提交的“产量”。每 30 次迭代后,就会加载一个新页面,并且接下来的 30 次提交将被“yield”。确实**迭代之间的依赖关系每三十次才发生一次**。 (2认同)
  • 我只是想说谢谢您提供了一个非常有用的生成器函数演示。我见过的所有其他例子都是人为的,并没有提供任何实际使用它们的充分理由。这是我见过的最好的例子,现在我完全理解为什么人们会使用它们。谢谢你! (2认同)

Ash*_*odi 6

正如您所说,Promise.all将一次性发送所有请求,然后在所有请求完成后您将收到响应。

在第二种情况下,您将一次性发送请求,但会一一收到响应。

请参阅此小示例以供参考。

let i = 1;
function somethingAsync(time) {
  console.log("fired");
  return delay(time).then(() => Promise.resolve(i++));
}
const items = [1000, 2000, 3000, 4000];

function delay(time) {
  return new Promise((resolve) => { 
      setTimeout(resolve, time)
  });
}

(async() => {
  console.time("first way");
  const promises = await Promise.all(items.map(e => somethingAsync(e)));
  for (const res of promises) {
    console.log(res);
  }
  console.timeEnd("first way");

  i=1; //reset counter
  console.time("second way");
  for await (const res of items.map(e => somethingAsync(e))) {
    // do some calculations
    console.log(res);
  }
  console.timeEnd("second way");
})();

Run Code Online (Sandbox Code Playgroud)

您也可以在这里尝试 - https://repl.it/repls/SuddenUselessAnalyst

希望这可以帮助。

  • 事实上你证明了我的第二点。使用“for wait”实际上会同时触发所有的 Promise,因此循环的执行时间为:4 秒。如果等待其中一个完成后再执行下一个,则总执行时间将为 1+2+3+4 = 10 秒。 (2认同)