в ожидании VS Promise.all - PullRequest
       1

в ожидании VS Promise.all

2 голосов
/ 11 января 2020

Есть ли разница между этим:

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

И этим?

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

Я знаю, что в первом фрагменте все обещания выполняются одновременно, но я Я не уверен насчет второго. Ожидает ли l oop первой итерации, чтобы вызвать следующее обещание? Или все обещания выполняются одновременно, и внутренняя часть l oop действует для них как обратный вызов?

Ответы [ 3 ]

2 голосов
/ 11 января 2020

Да, они абсолютно разные. for await предполагается использовать с асинхронными итераторами, а не с массивами уже существующих обещаний.

Просто чтобы прояснить,

for await (const res of items.map(e => somethingAsync(e))) …

работает так же, как

const promises = items.map(e => somethingAsync(e));
for await (const res of promises) …

или

const promises = [somethingAsync(items[0]), somethingAsync(items[1]), …);
for await (const res of promises) …

Звонки somethingAsync происходят немедленно, сразу, прежде чем что-либо ожидается. Затем они await редактируются один за другим, что, безусловно, является проблемой, если любой из них будет отклонен: это приведет к необработанной ошибке отказа от обещания. Использование Promise.all является единственным приемлемым вариантом для работы с массивом обещаний :

for (const res of await Promise.all(promises)) …

См. Ожидание более чем одной параллельной операции ожидания и Есть ли разница между await Promise.all () и множественным ожиданием? для подробностей.

0 голосов
/ 11 января 2020

На самом деле, использование синтаксиса for await запускает все обещания сразу.

Небольшой фрагмент кода доказывает это:

const sleep = s => {
  return new Promise(resolve => {
    setTimeout(resolve, s * 1000);
  });
}

const somethingAsync = async t => {
  await sleep(t);
  return t;
}

(async () => {
  const items = [1, 2, 3, 4];
  const now = Date.now();
  for await (const res of items.map(e => somethingAsync(e))) {
    console.log(res);
  }
  console.log("time: ", (Date.now() - now) / 1000);
})();

стандартный вывод: time: 4.001

Но внутренняя часть l oop не действует как «обратный вызов». Если я переверну массив, все журналы появятся одновременно. Я полагаю, что обещания выполняются сразу, и среда выполнения просто ждет, пока первый из них не разрешит go до следующей итерации.

РЕДАКТИРОВАТЬ: На самом деле, использование for await является плохой практикой, когда мы его используем с чем-то отличным от асинхронного итератора, лучше всего использовать Promise.all, согласно @Bergi в своем ответе.

0 голосов
/ 11 января 2020

Как вы сказали, Promise.all отправит все запросы в одном go, а затем вы получите ответ, когда все они будут выполнены.

Во втором сценарии вы отправите запрос в один go, но получайте ответ по одному.

См. этот небольшой пример для справки.

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");
})();

Вы также можете попробовать здесь - https://repl.it/repls/SuddenUselessAnalyst

Надеюсь, это поможет.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...