24

In node.js I need to use a function procesMultipleCandidates () which contains Array.foreach which process insert every element into db. but the entire function should return response after completing all insertion operation

JavaScript Code

async function procesMultipleCandidates (data) {
  let generatedResponse = []
  await data.forEach(async (elem) => {
    try {    
          
           // here candidate data is inserted into  
           let insertResponse = await insertionInCandidate(elem) 
           //and response need to be added into final response array 
           generatedResponse.push(insertResponse)
    } catch (error) {
    console.log('error'+ error);
    }
  })
  console.log('complete all') // gets loged first
  return generatedResponse // return without waiting for process of 
}

And as described above last return statement not waiting for the foreach execution to complete first.

Shankar
  • 2,890
  • 3
  • 25
  • 40
  • Your data.forEach itself blocks the execution until the callback finishes execution. You need not use 'await' at that point. – divine Aug 08 '18 at 04:41
  • this was helpful for me https://stackoverflow.com/questions/37576685/using-async-await-with-a-foreach-loop – frederj Oct 08 '20 at 17:30

4 Answers4

75

Use Array.prototype.map and Promise.all:

async function procesMultipleCandidates (data) {
  let generatedResponse = []
  await Promise.all(data.map(async (elem) => {
    try {
      // here candidate data is inserted into  
      let insertResponse = await insertionInCandidate(elem)  
      // and response need to be added into final response array 
      generatedResponse.push(insertResponse)
    } catch (error) {
      console.log('error'+ error);
    }
  }))
  console.log('complete all') // gets loged first
  return generatedResponse // return without waiting for process of 
}

Or use a for/of loop if you don't want the loop run concurrently:

async function procesMultipleCandidates (data) {
  let generatedResponse = []
  for(let elem of data) {
    try {
      // here candidate data is inserted into  
      let insertResponse = await insertionInCandidate(elem)  
      // and response need to be added into final response array 
      generatedResponse.push(insertResponse)
    } catch (error) {
      console.log('error'+ error);
    }
  }
  console.log('complete all') // gets loged first
  return generatedResponse // return without waiting for process of 
}
SimpleJ
  • 13,812
  • 13
  • 53
  • 93
9

Array.prototype.forEach() tries to execute sequentially and you may not always get the expected result.

Array.prototype.map() is the widely used for creating Promises using data and then resolved using await Promise.all([])

using .map() function, all the promises are resolved in parallel and if you happen to call an API you may end up getting error 429: Too many Requests

To execute all the promises sequentially, you can use for ...of.

const array1 = ['a', 'b', 'c'];

for (const element of array1) {
  console.log(element);
}

MDN Reference

Geet Choubey
  • 1,069
  • 7
  • 23
3

As far as I'm concern, for of works better with asynchrony than forEach. Anyway, in this case, if you only care for the aggregated result, I think this could be a solution.

async function procesMultipleCandidates(data) {
  const promises = data.map(insertionInCandidate);
  return await Promise.all(promises);
}
0

Use Promise.all instead, which will resolve once all Promises in the array passed to it have resolved. The syntax will probably be simplified if you use and return a plain Promise chain rather than async/await:

const procesMultipleCandidates = data => Promise.all(
  data.map(insertionInCandidate)
)
.catch(err => {
  console.log('err: ' + err);
});

Note that if there's an error, procesMultipleCandidates will currently return a resolved Promise rather than rejecting - it might be a better idea to have the consumer of procesMultipleCandidates handle errors, so that it can handle them appropriately (rather than simply console.logging it).

CertainPerformance
  • 356,069
  • 52
  • 309
  • 320
  • in your solution, how the response of each `insertionInCandidate()` can be pushed in one final response array? – Shankar Aug 08 '18 at 05:19
  • 1
    The `.map` is transforming each item in `data` into a `Promise` that resolves to the result of calling `insertionInCandidate` with the item - pass an array of those `Promise`s to `Promise.all`, and the `Promise.all` will resolve to an array of those values. No modifications to the code are needed - it's quite terse and simple. – CertainPerformance Aug 08 '18 at 05:22