如何在做其他事情之前回报许多承诺并且等待所有的承诺

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

这就是我想要的:

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

但我不确定语法。

134622 次浏览

你可以使用 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 有一篇关于承诺 给你的文章。我也在我的书 JavaScript: The New Toys JavaScript: 新玩具的第8章详细介绍了我的承诺,如果你感兴趣,可以在我的个人资料中找到链接。

这里有一个例子:

 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]

一个可重用的函数在这种模式下工作得很好:

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));
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 循环中有猫鼬查询,所以我把 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

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

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

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