Cómo devolver muchas promesas y esperarlas todas antes de hacer otras cosas

Resuelto Ganbin asked hace 9 años • 6 respuestas

Tengo un bucle que llama a un método que hace cosas de forma asincrónica. Este bucle puede llamar al método muchas veces. Después de este ciclo, tengo otro ciclo que debe ejecutarse solo cuando se hayan realizado todas las cosas asincrónicas.

Entonces esto ilustra lo que quiero:

for (i = 0; i < 5; i++) {
    doSomeAsyncStuff();    
}

for (i = 0; i < 5; i++) {
    doSomeStuffOnlyWhenTheAsyncStuffIsFinish();    
}

No estoy muy familiarizado con las promesas, ¿alguien podría ayudarme a lograrlo?

Así es como doSomeAsyncStuff()se comporta mi:

function doSomeAsyncStuff() {
    var editor = generateCKEditor();
    editor.on('instanceReady', function(evt) {
        doSomeStuff();
        // There should be the resolve() of the promises I think.
    })
}

Quizás tenga que hacer algo como esto:

function doSomeAsyncStuff() {
    var editor = generateCKEditor();
    return new Promise(function(resolve,refuse) {
        editor.on('instanceReady', function(evt) {
            doSomeStuff();
            resolve(true);
        });
    });
}

Pero no estoy seguro de la sintaxis.

Ganbin avatar Jul 15 '15 16:07 Ganbin
Aceptado

Puede usar Promise.all( spec , MDN ) para eso: acepta un montón de promesas individuales y le devuelve una única promesa que se resuelve cuando todas las que le dio se resuelven, o se rechaza cuando cualquiera de ellas es rechazada.

Entonces, si haces doSomeAsyncStuffuna promesa de devolución, entonces:

    const promises = [];
//  ^^^^^−−−−−−−−−−−−−−−−−−−−−−−−−−− use `const` or `let`, not `var`
    
    for (let i = 0; i < 5; i++) {
//       ^^^−−−−−−−−−−−−−−−−−−−−−−−− added missing declaration
        promises.push(doSomeAsyncStuff());
    }
    
    Promise.all(promises)
        .then(() => {
            for (let i = 0; i < 5; i++) {
//               ^^^−−−−−−−−−−−−−−−− added missing declaration
                doSomeStuffOnlyWhenTheAsyncStuffIsFinish();    
            }
        })
        .catch((e) => {
            // handle errors here
        });

MDN tiene un artículo sobre promesas aquí . También cubro las promesas en detalle en el Capítulo 8 de mi libro JavaScript: The New Toys , enlaces en mi perfil si estás interesado.

He aquí un ejemplo:

 function doSomethingAsync(value) {
     return new Promise((resolve) => {
         setTimeout(() => {
             console.log("Resolving " + value);
             resolve(value);
         }, Math.floor(Math.random() * 1000));
     });
   }
   
   function test() {
       const promises = [];
       
       for (let i = 0; i < 5; ++i) {
           promises.push(doSomethingAsync(i));
       }
       
       Promise.all(promises)
           .then((results) => {
               console.log("All done", results);
           })
           .catch((e) => {
               // Handle errors here
           });
   }
   
   test();
Expandir fragmento

Salida de muestra (debido a Math.random, lo que termina primero puede variar):

Resolviendo 3
Resolviendo 2
Resolviendo 1
Resolviendo 4
Resolviendo 0
Todo hecho [0,1,2,3,4]
T.J. Crowder avatar Jul 15 '2015 09:07 T.J. Crowder

Una función reutilizable funciona muy bien para este patrón:

function awaitAll(count, asyncFn) {
  const promises = [];

  for (i = 0; i < count; ++i) {
    promises.push(asyncFn());
  }

  return Promise.all(promises);
}

Ejemplo de OP:

awaitAll(5, doSomeAsyncStuff)
  .then(results => console.log('doSomeStuffOnlyWhenTheAsyncStuffIsFinished', results))
  .catch(e => console.error(e));

Un patrón relacionado es iterar sobre una matriz y realizar una operación asíncrona en cada elemento:

function awaitAll(list, asyncFn) {
  const promises = [];

  list.forEach(x => {
    promises.push(asyncFn(x));
  });

  return Promise.all(promises);
}

Ejemplo:

const books = [{ id: 1, name: 'foo' }, { id: 2, name: 'bar' }];

function doSomeAsyncStuffWith(book) {
  return Promise.resolve(book.name);
}

awaitAll(books, doSomeAsyncStuffWith)
  .then(results => console.log('doSomeStuffOnlyWhenTheAsyncStuffIsFinished', results))
  .catch(e => console.error(e));
2Toad avatar Oct 23 '2019 22:10 2Toad

/*** Worst way ***/
for(i=0;i<10000;i++){
  let data = await axios.get(
    "https://yourwebsite.com/get_my_data/"
  )
  //do the statements and operations
  //that are dependant on data
}

//Your final statements and operations
//That will be performed when the loop ends

//=> this approach will perform very slow as all the api call
// will happen in series


/*** One of the Best way ***/

const yourAsyncFunction = async (anyParams) => {
  let data = await axios.get(
    "https://yourwebsite.com/get_my_data/"
  )
  //all you statements and operations here
  //that are dependant on data
}
var promises = []
for(i=0;i<10000;i++){
  promises.push(yourAsyncFunction(i))
}
await Promise.all(promises)
//Your final statement / operations
//that will run once the loop ends

//=> this approach will perform very fast as all the api call
// will happen in parallal
Expandir fragmento

Sourav Purkait avatar Jul 04 '2020 13:07 Sourav Purkait
const doSomeAsyncStuff = async (funcs) => {
  const allPromises = funcs.map(func => func());
  return await Promise.all(allPromises);
}

doSomeAsyncStuff([
  () => new Promise(resolve => setTimeout(() => resolve(), 100)),
  () => new Promise(resolve => setTimeout(() => resolve(), 100)),
  () => new Promise(resolve => setTimeout(() => resolve(), 100)),
  () => new Promise(resolve => setTimeout(() => resolve(), 100)),
  () => new Promise(resolve => setTimeout(() => resolve(), 100)),
]);
JoeTidee avatar Apr 02 '2020 21:04 JoeTidee