Предположим, что по какой-то причине мы обходим массив с помощью метода map()
и нам нужно выполнить асинхронную функцию на каждом из элементов. Что-то вроде следующего:
const users = ['user1', 'user2', 'user3']
const data = users.map(async (user) => await fetchApiData(user))
Здесь нам нужно понять, что фиктивная функция fetchApiData
(которая получает в качестве параметра имя одного из пользователей) будет осуществлять асинхронный вызов внешнего API, с которым работает наше приложение.
Что произойдет, если мы теперь запишем содержимое переменной data
через консоль? Ну, мы получим что-то вроде следующего:
const users = ['user1', 'user2', 'user3']
const data = users.map(async (user) => await fetchApiData(user))
console.log(data)
/*
[
Promise { <pending> },
Promise { <pending> },
Promise { <pending> }
]
*/
То есть выполнение кода не останавливается в ожидании результатов внешнего вызова API, и поэтому массив data будет сформирован из такого количества элементов, сколько пользователей мы объявили в массиве users
и связали с каждой из позиций объект JavaScript Promise
в состоянии ожидания.
Разрешение всех вызовов параллельно.
Первый вариант решения этой проблемы — использовать метод all()
объекта JavaScript Promise
, который позволит всем обещаниям, которые он получает в качестве параметра (этот метод ожидает получить в качестве параметра массив обещаний, подлежащих разрешению), выполняться параллельно, не завися друг от друга, и не завершит свое выполнение, пока все эти обещания не завершатся.
const users = ['user1', 'user2', 'user3']
const data = await Promise.all(
users.map(async (user) => await fetchApiData(user))
)
console.log(data)
/*
[
dataFromUser1,
dataFromUser2,
dataFromUser3
]
*/
При параллельном выполнении мы должны понимать, что общее время выполнения метода будет соответствовать времени выполнения того обещания, которое потребляет больше времени на разрешение, что делает его очень быстрой альтернативой, когда нет зависимости между обещаниями.
Последовательное разрешение обещаний.
Но что произойдет, если у нас есть зависимость в выполнении обещаний, то есть вызовов асинхронной функции (в нашем примере внешний API будет зависеть от результата предыдущего вызова)? В этом случае мы должны использовать структуру for ... of
следующим образом:
const users = ['user1', 'user2', 'user3']
const data = []
for (const user of users) {
data.push(await fetchApiData(user))
}
console.log(data)
/*
[
dataFromUser1,
dataFromUser2,
dataFromUser3
]
*/
В этом случае легко понять, что, поскольку один вызов зависит от предыдущего, результатом выполнения всего кода будет сумма разрешения каждого из обещаний в отдельности.