Як повернути багато обіцянок і почекати їх усіх, перш ніж робити інші речі


85

У мене є цикл, який викликає метод, який робить речі асинхронно. Цей цикл може викликати метод багато разів. Після цього циклу у мене є ще один цикл, який потрібно виконувати лише тоді, коли всі асинхронні речі закінчені.

Отже, це ілюструє те, що я хочу:

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

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

Я не дуже обіцяний із обіцянками, тож хтось міг допомогти мені досягти цього?

Ось як doSomeAsyncStuff()поводиться моя :

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

Можливо, мені доведеться зробити щось подібне:

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

Але я не впевнений у синтаксисі.


Ви контролюєте асинхронні дзвінки? Вони вже повертають обіцянки, або ви можете змусити їх повернути обіцянки?
— TJ Crowder

Яка саме послідовність? Вам потрібно викликати інші функції після закінчення всіх попередніх асинхронних? Або вам просто потрібно викликати функцію після закінчення кожного асинхронізації?
— Sosdoc

Наразі перша функція не повертає обіцянок. Що я маю реалізувати. Я хочу відредагувати своє повідомлення, щоб додати деякі деталі робочого процесу моїх функцій. І так, мені потрібно, щоб усі матеріали першого циклу були закінчені перед початком виконання матеріалів у другому циклі.
— Ganbin

1
Щодо вашого редагування: "Можливо, мені доведеться зробити щось подібне" Так, дуже подібне, за винятком того, що немає sв кінці Promise.
— TJ Crowder

Відповіді:


161

Ви можете використовувати Promise.all( специфікацію , MDN ) для цього: він приймає купу індивідуальних обіцянок і повертає вам одну обіцянку, яка вирішується, коли вирішуються всі, які ви дали, або відхиляється, коли будь-яка з них відхиляється.

Отже, якщо ви doSomeAsyncStuffповернете обіцянку, тоді:

    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 є стаття на обіцянки тут . Я також детально висвітлюю промсіє в главі 8 моєї книги JavaScript: Нові іграшки , посилання в моєму профілі, якщо ви зацікавлені.

Ось приклад:

 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();

Вихідні дані (через те Math.random, що перше закінчується, може відрізнятися):

Вирішення 3
Вирішення 2
Вирішення 1
Вирішення 4
Вирішення 0
Все зроблено [0,1,2,3,4]

Добре, дякую, я спробую це зараз, і я приходжу із відгуками через кілька хвилин.
— Ганбін,

12
Ого, дякую, зараз я набагато більше розумію обіцянки. Я багато читав про обіцянки, але поки нам не потрібно використовувати їх у реальному коді, ми насправді не розуміємо всіх механізмів. Тепер я це покращую, і я можу почати писати круті речі завдяки вам.
— Ganbin

1
Крім того, якщо ви хочете, щоб ці завдання були виконані з будь-якої причини (наприклад, глузування прогресу), ви можете змінити Math.floor(Math.random() * 1000)на(i * 1000)
— OK, звичайно,

@TJ тепер, як я можу зробити дані результатів у поданні, і там я можу зробити цикл, щоб показати дані
— Ajit Singh

1
@ user1063287 - Ви можете зробити це, якщо код знаходиться в контексті, де awaitце дозволено. На даний момент єдине місце, яке ви можете використовувати, await- це всередині asyncфункції. (У якийсь момент ви також зможете використовувати його на верхньому рівні модулів.)
— TJ Crowder

5

Функція багаторазового використання чудово працює для цього шаблону:

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

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

  return Promise.all(promises);
}

Приклад OP:

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

Пов’язаний шаблон - це ітерація масиву та виконання асинхронної операції для кожного елемента:

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

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

  return Promise.all(promises);
}

Приклад:

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));

1
Це дійсно робить код легшим для розуміння та чистішим. Я не думаю, що поточний приклад (який, очевидно, був адаптований до кодексу OP) відповідає цій справедливості. Це акуратний фокус, дякую!
— Шон Вермаак,

2
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)),
]);

1

Ось код, який я написав для себе, щоб зрозуміти відповіді, викладені тут. У мене є мангустські запити у циклі for, тому я поставив тут, asyncFunctionщоб зайняти його місце. Сподіваюся, це комусь допоможе. Ви можете запустити цей скрипт у вузлі або будь-якому з багатьох середовищ виконання Javascript.

let asyncFunction = function(value, callback)
{
        setTimeout(function(){console.log(value); callback();}, 1000);
}



// a sample function run without promises

asyncFunction(10,
    function()
    {
        console.log("I'm back 10");
    }
);


//here we use promises

let promisesArray = [];

let p = new Promise(function(resolve)
{
    asyncFunction(20,
        function()
        {
            console.log("I'm back 20");
            resolve(20);
        }
    );
});

promisesArray.push(p);


for(let i = 30; i < 80; i += 10)
{
    let p = new Promise(function(resolve)
    {
        asyncFunction(i,
            function()
            {
                console.log("I'm back " + i);
                resolve(i);
            }
        );
    });
    promisesArray.push(p);
}


// We use Promise.all to execute code after all promises are done.

Promise.all(promisesArray).then(
    function()
    {
        console.log("all promises resolved!");
    }
)

0

/*** 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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.