27

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.

2
  • Your data.forEach itself blocks the execution until the callback finishes execution. You need not use 'await' at that point. Commented Aug 8, 2018 at 4:41
  • this was helpful for me stackoverflow.com/questions/37576685/… Commented Oct 8, 2020 at 17:30

4 Answers 4

78

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 
}
Sign up to request clarification or add additional context in comments.

2 Comments

using for/of is way simpler and works perfectly!
Hello! If it's not too late yet, I have a somewhat similar problem and I asked a question, could you help me please?
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

Comments

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

Comments

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).

2 Comments

in your solution, how the response of each insertionInCandidate() can be pushed in one final response array?
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 Promises 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.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.