Совет по JS: #1 – асинхронность/ожидание над массивами

Предположим, что по какой-то причине мы обходим массив с помощью метода 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
]
*/
Войти в полноэкранный режим Выйти из полноэкранного режима

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

Оцените статью
Procodings.ru
Добавить комментарий