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

IT技术 javascript asynchronous ecmascript-6 promise es6-promise
2021-02-07 21:23:32

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

所以这说明了我想要的:

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

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

我对Promise不是很熟悉,所以有人可以帮助我实现这一目标吗?

这是我的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);
        });
    });
}

但我不确定语法。

6个回答

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

所以如果你让doSomeAsyncStuffreturn 成为一个Promise,那么:

    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对Promise的文章在这里我还在我的书JavaScript: The New Toys 的第 8 章中详细介绍了 promsies,如果您感兴趣,可以在我的个人资料中找到链接。

下面是一个例子:

 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]
好的,谢谢,我现在试试这个,几分钟后我就会收到反馈。
2021-03-14 21:23:32
谢谢@Henke!我已经修复了链接。
2021-03-14 21:23:32
哇,非常感谢,现在我对 Promise 的了解更多了。我读了很多关于 Promise 的书,但是直到我们需要在实际代码中使用它们之前,我们并没有真正了解所有的机制。现在我变得更好了,我可以开始写很酷的东西了,谢谢你。
2021-03-21 21:23:32
@ user1063287 - 如果代码在await允许的上下文中,您可以这样做目前,您唯一可以使用的地方await是在async函数内部(在某些时候,您还可以在module的顶层使用它。)
2021-03-27 21:23:32
此外,如果您希望出于任何原因(例如模拟进度)按顺序完成这些任务,您可以更改Math.floor(Math.random() * 1000)(i * 1000)
2021-03-28 21:23:32

可重用函数非常适合这种模式:

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

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

  return Promise.all(promises);
}

操作示例:

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));
这确实使代码更易于理解和清洁。我认为当前的示例(显然已适应 OP 的代码)并没有做到这一点。这是一个巧妙的技巧,谢谢!
2021-03-14 21:23:32
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)),
]);

这是我为自己编写的代码,以便理解此处所述的答案。我在 for 循环中有 mongoose 查询,所以我把asyncFunction放在这里来代替它。希望它可以帮助任何人。您可以在 node 或许多 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!");
    }
)

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