如何返回许多承诺并在执行其他操作之前等待所有承诺


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

目前,第一个函数不返回promise。我必须执行。我想编辑消息以添加一些功能工作流程的详细信息。是的,我需要先完成第一个循环中的所有内容,然后再开始执行第二个循环中的内容。
甘彬2015年

1
重新编辑:“也许我必须做类似的事情”是的,非常类似于那样,除了s在末尾没有Promise
TJ Crowder

Answers:


160

您可以为此使用Promise.allspecMDN):它接受一堆单独的承诺,并给您带来一个单独的承诺,该承诺在您给它的所有承诺都得到解决时将得到解决,或者在任何一个被拒绝时都被拒绝。

因此,如果您做出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章中详细介绍Promies 。

这是一个例子:

 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]

好的,谢谢,我现在尝试一下,几分钟后就会收到反馈。
甘彬2015年

12
哇,非常感谢,现在我对Promise有了更多的了解,我读了很多关于Promise的内容,但是直到我们需要在真实代码中使用它们之前,我们才真正了解所有的机制。现在,我变得更好了,感谢您,我可以开始写一些很棒的东西。
甘彬2015年

1
此外,如果您出于任何原因(例如模拟进度)希望按顺序完成这些任务,则可以更改Math.floor(Math.random() * 1000)(i * 1000)
确定,请确保

@TJ现在我如何将结果数据呈现到视图,在那里我可以执行循环以显示数据
Ajit Singh

1
@ user1063287-如果代码在await允许的上下文中,则可以执行此操作。目前,您只能awaitasync函数内部使用。(在某些时候,您也可以在模块的顶层使用它。)
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的代码)做到了这一点。这是一个巧妙的技巧,谢谢!
Shaun Vermaak

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.