Главная » Хабрахабр » [Перевод] Как работать с async/await в циклах JavaScript

[Перевод] Как работать с async/await в циклах JavaScript

Как запустить асинхронные циклы по порядку или параллельно в JavaScript?

Перед тем, как делать асинхронную магию, я хочу напомнить как выглядят классические синхронные циклы.

Синхронные циклы

Очень давно я писал циклы таким способом (возможно вы тоже):

for (var i=0; i < array.length; i++) { var item = array[i]; // делаем что-нибудь с item
}

Но у него много проблем с читаемостью и с поддержкой. Этот цикл хороший и быстрый. Через некоторое время я привык его лучшей версии:

array.forEach((item) => { // делаем что-нибудь с item
});

Появляются новые фичи и синтаксис. Язык JavaScript развивается очень быстро. Одна из моих любых улучшений это async/await.

И иногда встречаются ситуации, когда мне нужно что-либо сделать с элементами массива асинхронно. Сейчас я использую этот синтакс достаточно часто.

Асинхронные циклы

Давайте просто попробуем написать асинхронную функцию и ожидать задачу обработки каждого элемента: Как использовать await в теле циклы?

async function processArray(array) )
}

Почему? Этот код выдаст ошибку. Как вы можете видеть processArray — это асинхронная функция. Потому что мы не можем использовать await внутри синхронной функции. Но анонимная функция, которую мы используем для forEach, является синхронной.

Что можно с этим сделать?

1. Не дожидаться результата выполнения

Мы можем определить анонимную функцию как асинхронную:

async function processArray(array) { array.forEach(async (item) => { await func(item); }) console.log('Done!');
}

forEach — синхронная операция. Но forEach не будет дожидаться выполнения завершения задачи. Проверим на простом тесте: Она просто запустить задачи и пойдет дальше.

function delay() { return new Promise(resolve => setTimeout(resolve, 300));
} async function delayedLog(item) { // мы можем использовать await для Promise // который возвращается из delay await delay(); console.log(item);
}
async function processArray(array) { array.forEach(async (item) => { await func(item); }) console.log('Done!');
} processArray([1, 2, 3]);

В консоли мы увидим:

Done!
1
2
3

Но всё же в большинстве вариантов это не подходящая логика. В некоторых ситуация это может быть нормальным результатом.

2. Обработка цикла последовательно

Но в этот раз мы будем использовать его новую версию с конструкцией for..of (Спасибо Iteration Protocol): Чтобы дождаться результата выполнения тела цикла нам нужно вернуться к старому доброму циклу "for".

async function processArray(array) { for (const item of array) { await delayedLog(item); }) console.log('Done!');
}

Это даст нам ожидаемый результат:

1
2
3
Done!

Но мы может запустить цикл параллельно! Каждый элемент массива будет обработан последовательно.

3. Обработка цикла параллельно

Нужно слегка изменить код, чтобы запустить операции параллельно:

async function processArray(array) { // делаем "map" массива в промисы const promises = array.map(delayedLog); // ждем когда всё промисы будут выполнены await Promise.all(promises); console.log('Done!');
}

Но будьте аккуратны с большими массивами. Этот код запустить несколько delayLog задач параллельно. Слишком много задач может быть слишком тяжело для CPU и памяти.

Этот код не гарантирует параллельного исполнения. Так же, пожалуйста, не путайте "параллельные задачи" из примера с реальной параллельностью и потоками. Запросы сети, webworkers и некоторые другие задачи могуть быть выполнены параллельно. Всё завесит от тела цикла (в примере это delayedLog).


Оставить комментарий

Ваш email нигде не будет показан
Обязательные для заполнения поля помечены *

*

x

Ещё Hi-Tech Интересное!

MIDI-router на Raspberry Pi

Хочу рассказать о том, как решить проблему, которая наверняка знакома любителям аппаратных синтезаторов. Причем, по понятным причинам хочется все это сделать не используя компьютер. Что делать, если хочется состыковать MIDI-контроллер и синтезатор, но у одного из них есть только USB ...

DynamicData: Изменяющиеся коллекции, шаблон проектирования MVVM и реактивные расширения

В феврале 2019 года состоялся релиз ReactiveUI 9  —  кроссплатформенного фреймворка для построения приложений с графическим пользовательским интерфейсом на платформе Microsoft .NET. ReactiveUI  — это инструмент для тесной интеграции реактивных расширений с шаблоном проектирования MVVM. Знакомство с фреймворком можно начать с ...