0

我有一个 nodejs 函数processReviews(workflow),当被调用时,它应该将多个承诺推送到一个数组,然后在 for 循环promises[]之后运行它们。promises.all()

function examplePromiseFunc(){
    return new Promise((resolve, reject) => {
        console.log("examplePromiseFunc() INSIDE ")
        resolve('done')
    })
}

async function processReviews(workflow){
        //get objects from s3
        let allObjects = await getAllObjects(workflow);
        allObjects = allObjects.filter(obj => obj.Key.includes('output.json'))
        console.log(`found ${allObjects.length} .json files.`)

        const promises = [];
        for (let i = 0; i < allObjects.length; i++) {
            console.log('i=',i,' pushing to promises[]')
            promises.push( examplePromiseFunc() )
        }

        const result = await Promise.all(promises)
        console.log('running, result = ', result);
}

但是当我运行我的代码时,输​​出如下所示:

found 697 .json files.
i= 0  pushing to promises[]
examplePromiseFunc() INSIDE
i= 1  pushing to promises[]
examplePromiseFunc() INSIDE
i= 2  pushing to promises[]
examplePromiseFunc() INSIDE
i= 3  pushing to promises[]
examplePromiseFunc() INSIDE
...

这意味着每次我向我的 promises[] 数组 ( promises.push( await examplePromiseFunc() )) 推送一个 promise 时,该函数examplePromiseFunc()都会立即被调用并且不会等待。

我希望我的函数只在我await Promise.all(promises)最后运行时被调用,有什么我遗漏的吗?我的异步函数会导致问题吗?我一直在阅读 javascript promises.all ,这似乎是一个很好的实现。

4

3 回答 3

1

问题是您已经await在循环内部使用,这意味着循环将“等待”并按顺序处理项目。

相反,您应该只将承诺添加到数组中,然后await像您一样在最后添加所有承诺:

async function processReviews(workflow) {
  //get objects from s3
  const allObjects = await getAllObjects(workflow);

  const promises = [];
  for (let i = 0; i < allObjects.length; i++) {
    // Don't await the promise here, just start it and add it to the array.
    promises.push(examplePromiseFunc(allObjects[i]));
  }
  const result = await Promise.all(promises)
  console.log(result);
        
}
于 2021-01-21T00:53:13.073 回答
1

这里对Promise构造函数的工作方式存在根本性的误解。

构造函数采用单个函数参数,称为executor

new Promise( executor)

executor 函数在构造过程中被同步调用,两个函数参数通常称为resolvereject

executor( resolve, reject)

Promise 执行器(通常)负责启动异步操作,并使处理操作完成和错误处理的代码可以使用resolvereject函数,通常在回调函数中。

因此代码

    for (let i = 0; i < allObjects.length; i++) {
        console.log('i=',i,' pushing to promises[]')
        promises.push( examplePromiseFunc() )
    }

examplePromiseFunct多次调用,并且在该函数中,返回的承诺的执行程序Promise在构造期间被同步调用(通过)。因此日志正如人们所期望的那样:每次examplePromiseFunc调用“examplePromiseFunc() INSIDE”的日志。

这种误解很可能会引出第二个:

Promise.all不“运行”承诺 - 承诺是被动对象,它们以确定的方式响应调用其关联resolvereject函数,通过调用附加到它们的已履行或拒绝处理程序 - 或者如果通过承诺解决,则链接另一个承诺。

Promise.all简单地返回一个承诺,该承诺通过其参数承诺的已履行结果数组实现,或者因拒绝原因或其参数数组中的第一个被拒绝的承诺而被拒绝。它在本机代码中有一个执行器,可以有效地将then处理程序附加到其参数数组中的 Promise,然后被动地等待(即它返回到事件循环)直到参数 Promise 一次解决一个。

于 2021-01-21T01:49:06.293 回答
0

那是因为 Promise 只能以这种方式工作。当您将一个承诺推入一个数组时,您已经在等待它(在循环内),即如果您不等待它们执行,那么它们也会执行,有或没有await promise.all,也有可能所有在你在 promise.all 中传递该数组之前,promise 已经解决。下面的函数也可以在没有全部承诺的情况下解决所有承诺。

async function processReviews(workflow){
        //get objects from s3
        let allObjects = await getAllObjects(workflow);
        allObjects = allObjects.filter(obj => obj.Key.includes('output.json'))
        console.log(`found ${allObjects.length} .json files.`)

        const promises = [];
        for (let i = 0; i < allObjects.length; i++) {
            console.log('i=',i,' pushing to promises[]')
            promises.push( examplePromiseFunc() )
        }
}

此外,您不应无限制地使用 promise.all,因为它可能会达到您的硬件限制。使用有限制的地图可以减少您的问题。

async function processReviews(workflow) {
  //get objects from s3
  let allObjects = await getAllObjects(workflow);
  allObjects = allObjects.filter(obj => obj.Key.includes("output.json"));
  console.log(`found ${allObjects.length} .json files.`);

  for (let i = 0; i < allObjects.length; i = i + PROMISE_LIMIT) {
    const objects = allObjects.slice(i, i + PROMISE_LIMIT);
    const result = await Promise.all(objects.map(elem => examplePromiseFunc()));
    console.log("running, result = ", result);
  }
}
于 2021-01-21T02:21:37.997 回答