Карта Async Await не ожидает завершения функции async внутри функции карты перед сопоставлением следующего элемента

У меня есть массив, который я сопоставляю, внутри функции карты я вызываю асинхронную функцию, которая выполняет асинхронный запрос, возвращающий обещание, используя request-promise.

Я ожидаю, что первый элемент массива будет отображен, выполнит запрос, а затем второй элемент повторит тот же процесс. Но в данном случае это не так.

Это моя функция;

const fn = async() => {
  const array = [0, 1, 2];
  console.log('begin');
  await Promise.all(array.map(item => anAsyncFunction(item)));
  console.log('finished');
  return;
}

anAsyncFunction как следует;

const anAsyncFunction = async item => {
  console.log(`looping ${item}`);
  const awaitingRequest = await functionWithPromise(item);
  console.log(`finished looping ${item}`);
  return awaitingRequest;
}

И functionWithPromise где делается запрос

const functionWithPromise = async (item) => {
  console.log(`performing request for ${item}`);
  return Promise.resolve(await request(`https://www.google.com/`).then(() => {
    console.log(`finished performing request for ${item}`);
    return item;
  }));
}

Из журналов консоли я получаю;

begin
looping 0
performing request for 0
looping 1
performing request for 1
looping 2
performing request for 2
finished performing request for 0
finished looping 0
finished performing request for 1
finished looping 1
finished performing request for 2
finished looping 2
finished

Однако я хочу

begin
looping 0
performing request for 0
finished performing request for 0
finished looping 0
looping 1
performing request for 1
finished performing request for 1
finished looping 1
looping 2
performing request for 2
finished performing request for 2
finished looping 2
finished

Обычно меня устраивает этот шаблон, но мне кажется, что я получаю некорректное тело из вызова запроса, так как я могу сделать слишком много одновременно.

Есть ли лучший метод для того, чего я пытаюсь достичь?

2 ответа

Решение

.map()не поддерживает асинхронность или обещание. Он просто послушно принимает значение, которое вы возвращаете из его обратного вызова, и помещает его в массив результатов. Даже если в вашем случае это обещание, оно все равно продолжается, не дожидаясь этого обещания. И в этом отношении вы ничего не можете сделать, чтобы изменить .map()поведение. Так оно и работает.

Вместо этого используйте for петля, а затем await ваша асинхронная функция внутри цикла, и это приостановит цикл.


Ваша структура:

await Promise.all(array.map(item => anAsyncFunction(item)));

работает все anAsyncFunction() звонит параллельно, а затем ждет, пока все они закончатся.


Чтобы запускать их последовательно, используйте for петля и await индивидуальный вызов функции:

const fn = async() => {
  const array = [0, 1, 2];
  console.log('begin');
  for (let item of array) {
      await anAsyncFunction(item);
  }
  console.log('finished');
  return;
}

Важно знать, что ни один из методов итерации массива не поддерживает асинхронность. Это включает .map(), .filter(), .forEach()и т. д. Итак, если вы хотите дождаться чего-то внутри цикла, чтобы упорядочить ваши асинхронные операции, используйте обычный for петля, которая async осведомлен и приостановит цикл.

Вы можете попробовать заменить эту строку:

      await Promise.all(array.map(item => anAsyncFunction(item)));

с:

      await Promise.all(array.map(async(item) => await anAsyncFunction(item)));

Должен работать больше nodejs, чем для альтернативы цикла, только forEach должен быть запрещен в этом случае.

Другие вопросы по тегам