Работ асинхронных циклов в JavaScript

Асинхронные операции сбивают с толку многих разработчиков.
Сочетание асинхронного цикла с циклом for (или for…of), возможно, является наиболее простым вариантом при выполнении асинхронных операций над элементами массива. Использование await внутри цикла for приведет к остановке кода и ожиданию завершения асинхронного выполнения кода, это означает, что все промисы будут выполняться последовательно.

const asyncUppercase = item =>
  new Promise(resolve =>
    setTimeout(
      () => resolve(item.toUpperCase()),
      Math.floor(Math.random() * 1000)
    )
  );

const uppercaseItems = async () => {
  const items = ['a', 'b', 'c'];
  for (item of items) {
    const uppercaseItem = await asyncUppercase(item);
    console.log(uppercaseItem);
  }

  console.log('Items processed');
};

uppercaseItems();
// LOGS: 'A', 'B', 'C', 'Items processed'

Промисы
Promise.all() предоставляет еще один вариант для асинхронных циклов по массивам. Основное отличие от предыдущего примера заключается в том, что Promise.all() выполняет все асинхронные операции параллельно. Это означает, что промисы будут выполняться не по порядку, что в некоторых случаях может быть проблемой. Чаще всего это предпочтительное решение, поскольку довольно редко требуется, чтобы промисы выполнялись последовательно.

const asyncUppercase = item =>
  new Promise(resolve =>
    setTimeout(
      () => resolve(item.toUpperCase()),
      Math.floor(Math.random() * 1000)
    )
  );

const uppercaseItems = () => {
  const items = ['a', 'b', 'c'];
  return Promise.all(
    items.map(async item => {
      const uppercaseItem = await asyncUppercase(item);
      console.log(uppercaseItem);
    })
  ).then(() => {
    console.log('Items processed');
  });
};
// LOGS: 'A', 'C', 'B', 'Items processed'

Методы массива
К сожалению, методы массива, такие как Array.prototype.forEach(), плохо работают с async/await. Единственное жизнеспособное решение — использовать Promise.all(), как показано в предыдущем примере. Использование асинхронного обратного вызова с Array.prototype.forEach() приведет к тому, что часть кода будет выполняться, но awaite работать не будет.

const asyncUppercase = item =>
  new Promise(resolve =>
    setTimeout(
      () => resolve(item.toUpperCase()),
      Math.floor(Math.random() * 1000)
    )
  );

const uppercaseItems = async () => {
  const items = ['a', 'b', 'c'];
  await items.forEach(async item => {
    const uppercaseItem = await asyncUppercase(item);
    console.log(uppercaseItem);
  });

  console.log('Items processed');
};

uppercaseItems();
// LOGS: ''Items processed', 'B', 'A', 'C'

Ответить