Как использовать async/await с map в js
В какой-то момент вы, возможно, задавались вопросом, как использовать асинхронные функции в таких методах, как .map или .forEach, поскольку в этом небольшом блоге вы увидите наиболее распространенные ошибки и способы их решения.
Решение
Для этого в файле index.ts у нас будет следующий базовый код:
const usernames: string[] = ["jordanrjdev", "anonymous123", "channelyy"]; const simulateFetchData = (username: string): Promise<string> => { return new Promise((resolve) => { setTimeout(() => { resolve(`${username} is a valid username`); }, 1000); }); }
Как видите, у нас есть массив имен пользователей и функция, которая принимает параметр и возвращает строку.
Теперь мы будем перебирать массив имен пользователей, чтобы получить смоделированные данные каждого пользователя с помощью метода карты:
const dataUsers = usernames.map(async (username) => { return await simulateFetchData(username); }); console.log(dataUsers);
Но при выполнении мы увидим в консоли следующий результат:
[ Promise { <pending> }, Promise { <pending> }, Promise { <pending> } ]
Итак, чтобы решить эту проблему, у нас есть два варианта: использовать Promise.all или использовать for of.
For of
Мы собираемся использовать for, чтобы устранить эту очень распространенную ошибку, из-за которой мы можем потерять много времени при попытке найти наиболее подходящее решение.
const getWithForOf = async() => { console.time("for of"); const data = [] for (const username of usernames) { let dataUser = await simulateFetchData(username); data.push(dataUser); } console.timeEnd("for of"); } getWithForOf();
В этом случае код будет выполняться последовательно, поэтому вы можете ждать каждого вызова. Это поможет нам решить каждую итерацию, прежде чем перейти к следующей.
Имейте в виду, что если мы выполняем N итераций, и для решения каждой из них требуется 1 секунда, как в нашем примере, это означает, что в общей сложности потребуется 3 секунды, чтобы завершить выполнение этой части кода. Мы можем видеть это в выводе консоли благодаря console.time:
for of: 3,012s
Promise.all
Теперь мы будем использовать метод Promise.all для решения нашей проблемы, поэтому у нас будет следующий код:
const getWithPromiseAll = async() => { console.time("promise all"); let data = await Promise.all(usernames.map(async (username) => { return await simulateFetchData(username); })) console.timeEnd("promise all"); } getWithPromiseAll();
Как вы можете видеть, у нас есть метод Promise.all, который получает массив promises, помните, что он
usernames.map(async (username) => {return await simulateFetchData(username);})
возвращает массив промисов, как раз то, что нам нужно, поэтому мы передаем его в Promise.all для их разрешения.
Этот метод приведет к параллельному разрешению всего асинхронного кода.
Итак, в отличие от for, давайте посмотрим, сколько времени потребуется для выполнения этой функции в консоли:
promise all: 980.3000000119209ms
То есть, если у нас есть количество N асинхронных функций, они будут выполняться и разрешаться без ожидания между ними, что может пригодиться в каком-то конкретном случае.
Иногда по соображениям производительности нам нужно будет выполнять наши промисы параллельно с Promise.all, а иногда нам нужно будет делать это последовательно с циклом for. Важно то, что вы понимаете разницу между каждым из них и то, как адаптировать его к вашим потребностям.
Если у вас есть какие-либо вопросы или предложения, не забудьте оставить комментарий, до скорой встречи :)