简体   繁体   English

为什么 Array.map Array.forEach 异步操作需要 Promise.all?

[英]Why is Promise.all needed for Array.map Array.forEach asynchronous operation?

I am using NodeJS (ExpressJS) server, have the following code:我正在使用 NodeJS (ExpressJS) 服务器,具有以下代码:

let caseScore = 0
await questions.map(async q => {
  caseScore += await q.grade() // Queries database and gets questions, finds score
}))
console.log(caseScore)
>> Output: 0

However, it appears that q.grade() finishes executing after the request is finished.但是,似乎 q.grade() 在请求完成后完成执行。 I put a console.log() within q.grade, and it shows up after response is sent.我在 q.grade 中放置了一个 console.log(),它在发送响应后显示。 Clearly this is executed asynchronously.显然这是异步执行的。 So later I did this:所以后来我这样做了:

let caseScore = 0
await Promise.all(questions.map(async q => {
  caseScore += await q.grade() // Queries database and gets questions, finds score
})))
console.log(caseScore)
>> Output: 2

It works perfectly.它完美地工作。 Can someone explain to me why Promise.all is needed?有人可以向我解释为什么需要 Promise.all 吗? Also, if I switch.map for.forEach, Promise.all errors, what is the correct way to do this for.forEach?另外,如果我切换.map for.forEach,Promise.all 错误,那么执行此 for.forEach 的正确方法是什么?

You should have to wait for all async calls to be completed.您应该等待所有异步调用完成。 That is the reason why promise.all needed.这就是需要promise.all的原因。

It converts the set of promises to a single promise.它将一组承诺转换为单个 promise。

Array.prototype.map works like that. Array.prototype.map 就是这样工作的。

Array.prototype.map = function(callback) {
  var results = [];
  for (var i = 0; i < this.length; i++) {
    results.push(callback(this[i], i));
  }
  return result;
}

In your code your callback returns Promise and Array.map returns array of Promise .在您的代码中,您的回调返回Promise和 Array.map 返回数组Promise Because it doesn't awaits for callback to be completed.因为它不等待回调完成。 Array.forEach also will not work for you because it doesn't awaits callback too. Array.forEach 也不适合你,因为它也不等待回调。

The best way to solve your problem is using Promise.all to convert array of Promise into a single Promise and await it.解决问题的最佳方法是使用Promise.allPromise数组转换为单个Promise并等待它。

await Promise.all(questions.map(async q => {
  caseScore += await q.grade();
}));

Or using simple for loop like that.或者像这样使用简单for循环。

for (var q of questions) {
  caseScore += await q.grade();
}

When you await an array of promises, you wait for an array which is not a promise (even though it contain promises inside).当您await一系列承诺时,您等待的不是 promise 的数组(即使其中包含承诺)。 await will immediately resolve values that are not a Promise . await将立即解析不是Promise的值。 It's like if you did await 'hello' , that would be resolving instantly.就像您确实await 'hello' ,那将立即解决。

Promise.all is a util that exposes a new Promise that resolves only when all the promises in the array passed as an argument are resolved. Promise.all是一个实用程序,它公开了一个新的Promise ,它仅在作为参数传递的数组中的所有承诺都被解析时才解析。 Since it's creates it's own promise, you can await on Promise.all .由于它创建了自己的 promise,因此您可以在Promise.allawait

[EDIT] Careful, do not use await in loops like this [编辑] 小心,不要在这样的循环中使用 await

for (var q of questions) {
  caseScore += await q.grade();
}

The reason is that it translates to原因是它翻译为

questions[0].grade().then(score => 
  return questions[1].grade().then(score => 
    return questions[2].grade().then(score => 
      return questions[3].grade().then(score => 
        // ...
      );
    );
  );
);

it creates a lots of context in memory and makes everything serial which is not taking advantage of javascript async nature它在 memory 中创建了很多上下文,并使所有没有利用 javascript 异步性质的东西成为串行

if you want to have a sum or some values shared between promises you can use a context object如果您想在 Promise 之间共享总和或某些值,您可以使用上下文 object

 async function grade1() { this.sum += await getSumFromServer(); this.nbOfTasks += 1; } async function grade2() { this.sum += await getSumFromServer(); this.nbOfTasks += 1; } async function grade3() { this.sum += await getSumFromServer(); this.nbOfTasks += 1; } async function main() { const context = { sum: 0, nbOfTasks: 0, } const promisesWithContext = [grade1, grade2, grade3].map(fn => fn.call(context)); await Promise.all(promisesWithContext); console.log(context); } main(); // STUB function getSumFromServer() { return new Promise((resolve) => { setTimeout(() => { resolve(Math.random() * 100) }, 1000) }); }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM