Como trabalhar com assíncrono / aguardar em loops JavaScript

Como executar loops assíncronos em ordem ou em paralelo no JavaScript?


Antes de fazer mágica assíncrona, quero lembrá-lo de como são os loops síncronos clássicos.


Loops síncronos


Há muito tempo, escrevi loops dessa maneira (talvez você também):


for (var i=0; i < array.length; i++) { var item = array[i]; //  -  item } 

Este ciclo é bom e rápido. Mas ele tem muitos problemas com legibilidade e suporte. Depois de um tempo, me acostumei com sua melhor versão:


 array.forEach((item) => { //  -  item }); 

JavaScript está se desenvolvendo muito rápido. Novos recursos e sintaxe são exibidos. Uma das minhas melhorias favoritas é assíncrona / aguardar .


Agora eu uso essa sintaxe com bastante frequência. E, às vezes, há situações em que preciso fazer algo com elementos da matriz de forma assíncrona.


Loops assíncronos


Como usar await no corpo do loop? Vamos apenas tentar escrever uma função assíncrona e esperar a tarefa de processar cada elemento:


 async function processArray(array) { array.forEach(item => { //       //     ! await func(item); }) } 

Este código gerará um erro. Porque Porque não podemos usar await dentro de uma função síncrona. Como você pode ver, processArray é uma função assíncrona. Mas a função anônima que usamos para forEach é síncrona .


O que pode ser feito sobre isso?


1. Não espere pelo resultado da execução


Podemos definir uma função anônima como assíncrona:


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

Mas o forEach não aguardará a conclusão da tarefa. forEach é uma operação síncrona. Ela simplesmente iniciará as tarefas e seguirá em frente. Vamos verificar um teste simples:


 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 delayedLog(item); }) console.log('Done!'); } processArray([1, 2, 3]); 

No console, veremos:


 Done! 1 2 3 

Em algumas situações, este pode ser um resultado normal. Mas ainda assim, na maioria dos casos, essa não é uma lógica adequada.


2. Processando o Ciclo Sequencialmente


Para aguardar o resultado da execução do corpo do loop, precisamos retornar ao bom e velho loop "for". Mas desta vez usaremos sua nova versão com o construtor for..of (graças ao protocolo de iteração ):


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

Isso nos dará o resultado esperado:


 1 2 3 Done! 

Cada elemento da matriz será processado sequencialmente. Mas podemos executar o loop em paralelo!


3. Processando o loop em paralelo


Você precisa modificar levemente o código para iniciar as operações em paralelo:


 async function processArray(array) { //  "map"    const promises = array.map(delayedLog); //       await Promise.all(promises); console.log('Done!'); } 

Este código pode executar várias tarefas delayLog em paralelo. Mas tenha cuidado com grandes matrizes. Muitas tarefas podem ser muito difíceis para a CPU e a memória.


Além disso, não confunda as "tarefas paralelas" do exemplo com paralelismo e threads reais. Este código não garante execução paralela. Tudo depende do corpo do loop (no exemplo, é delayedLog ). Solicitações de rede, trabalhadores da web e algumas outras tarefas podem ser executadas em paralelo.

Source: https://habr.com/ru/post/pt435084/


All Articles