如何返回许多 Promise 并在做其他事情之前等待它们

How to return many Promises and wait for them all before doing other stuff

提问人:Ganbin 提问时间:7/15/2015 最后编辑:HenkeGanbin 更新时间:5/21/2022 访问量:152972

问:

我有一个循环,它调用一个异步执行操作的方法。此循环可以多次调用该方法。在这个循环之后,我还有另一个循环,只有在所有异步操作完成后才需要执行。

所以这说明了我想要什么:

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

但我不确定语法。

JavaScript 异步 ecmascript-6 es6-promise

评论

0赞 T.J. Crowder 7/15/2015
您是否控制了异步调用?他们是否已经返回了承诺,或者你能让他们返回承诺吗?
0赞 Vale 7/15/2015
序列到底是什么?在完成所有以前的异步函数后,是否需要调用其他函数?或者你只需要在每个异步完成后调用一个函数?
0赞 Ganbin 7/15/2015
目前,第一个函数不返回 promises。我必须实施。我想编辑我的消息,以添加我的函数工作流的一些细节。是的,我需要在开始执行第二个循环中的内容之前完成第一个循环的所有内容。
1赞 T.J. Crowder 7/15/2015
回复你的编辑:“也许我必须做这样的事情”是的,非常像那样,除了末尾没有.sPromise

答:

260赞 T.J. Crowder 7/15/2015 #1

你可以使用 (specMDN):它接受一堆单独的 promise,并给你一个 promise ,当你给它的所有 promise 都得到解决时,这个 promise 就会得到解决,或者当其中任何一个被拒绝时,这个 promise 就会被拒绝。Promise.all

因此,如果您做出退货承诺,那么: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 有一篇关于承诺的文章我还在我的书《JavaScript:新玩具》的第 8 章中详细介绍了 propromises,如果您有兴趣,可以在我的个人资料中链接。

下面是一个示例:

 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

Resolving 3
Resolving 2
Resolving 1
Resolving 4
Resolving 0
All done [0,1,2,3,4]

评论

0赞 Ganbin 7/15/2015
好的,谢谢,我现在尝试这个,我会在几分钟内提供反馈。
14赞 Ganbin 7/15/2015
哇,非常感谢,现在我更了解了这些承诺。我读了很多关于承诺的文章,但是在我们需要在实际代码中使用它们之前,我们并不真正理解所有的机制。现在我变得更好了,我可以开始写很酷的东西了,多亏了你。
1赞 OK sure 1/12/2018
此外,如果您出于任何原因(例如模拟进度)想要按顺序完成这些任务,则可以更改为Math.floor(Math.random() * 1000)(i * 1000)
0赞 noobCoder 3/7/2019
如果返回一个承诺,如何等待它与循环中的其他人一起完成?doSomethingAsync(i)
1赞 T.J. Crowder 7/24/2019
@user1063287 - 如果代码位于允许的上下文中,则可以执行此操作。目前,您唯一可以使用的地方是函数内部。(在某些时候,您还可以在模块的顶层使用它。awaitawaitasync
14赞 2Toad 10/24/2019 #2

可重用的函数适用于此模式:

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

评论

2赞 Shaun Vermaak 6/8/2020
这确实使代码更易于理解和更简洁。我不认为当前的例子(显然适应了 OP 的代码)做到了这一点。这是一个巧妙的技巧,谢谢!
3赞 JoeTidee 4/3/2020 #3
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)),
]);
3赞 842Mono 6/5/2020 #4

这是我为自己编写的代码,以便理解此处所述的答案。我在 for 循环中有猫鼬查询,所以我把 to 放在这里来代替它。希望它对任何人有所帮助。您可以在 node 或许多 Javascript 运行时中的任何一个运行此脚本。asyncFunction

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!");
    }
)
4赞 Sourav Purkait 7/4/2020 #5

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

0赞 natanavra 7/15/2021 #6

如果您想多次做同样的事情,这里有一个优雅的解决方案:

await Promise.all(new Array(10).fill(0).map(() => asyncFn()));

这将创建一个包含 10 个项目的数组,用零填充它,然后将其映射到一个 promise 数组。