Почему возникает проблема суммирования времени при использовании await в цикле и как это исправить
Что проверяют
Понимаете ли вы, что await приостанавливает только текущую асинхронную функцию, и отличаете ли случай, где последовательность обязательна, от случая, где она получилась случайно.
Как отвечать
В цикле с await следующий запрос отправляется только после ответа предыдущего, поэтому общее время складывается из всех ожиданий. Ключ к решению в том, что промис начинает работу в момент создания, а await лишь ждёт результата. Значит, промисы надо сначала создать все, а ждать потом:
const results = await Promise.all(items.map((item) => load(item)));Теперь время близко к самому долгому запросу, а не к сумме. Дальше стоит проговорить оговорки, за них и ставят плюс:
Promise.allотклоняется на первой же ошибке, остальные результаты теряются. Когда нужны все исходы, беритеPromise.allSettled;- неограниченная параллельность это своя проблема: сотни одновременных запросов упрутся в лимит соединений браузера и в защиту сервера от частых обращений. Нужен пул, то есть обработка порциями или очередь с ограничением;
- последовательность иногда обязательна: пагинация по курсору, запись с зависимостью от предыдущего шага, жёсткое ограничение частоты на стороне интерфейса сервера. Тогда цикл с
awaitи есть правильный код, и это стоит сказать вслух.
Где спотыкаются
Переписывают цикл на forEach с асинхронным колбэком. Метод ничего не ждёт, функция возвращает управление до готовности данных, а отказ промиса превращается в необработанное исключение. Для последовательного обхода нужен for...of, для параллельного map вместе с Promise.all.
Распараллеливают всё подряд без ограничения. На десятке элементов разницы не видно, на нескольких тысячах браузер выстраивает очередь сам, сервер отвечает отказами, и время становится хуже исходного.
Считают, что Promise.all ускоряет любой код. Он ускоряет только ожидание. Если время уходит на разбор ответов или на перерисовку, единственный поток остаётся узким местом, а настоящее решение часто лежит в другой плоскости: один пакетный запрос вместо множества мелких.