Карта 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 должен быть запрещен в этом случае.