4

I have an asynchronous problem here. The forEach is still running when res.json({ errors }); returns so all the errors aren't picked up. How do I deal with this?

router.post('/new/user', async function (req, res, next) { const validateEmail = async (email) => { var re = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; return re.test(email); }; const reqBody = { email, password }; let errors = {}; Object.keys(reqBody).forEach(async (field) => { if (field === 'email' && validateEmail(await reqBody[field])) { errors = { ...errors, [field]: 'Not a valid Email' }; } console.log(3); if (field === 'password' && password !== '' && password < 4) { errors = { ...errors, [field]: 'Password too short' }; } }); if (Object.keys(errors).length > 0) { res.json({ errors }); } } 
2
  • 2
    You can't make a .forEach() loop wait for an asynchronous operation inside of it. It just isn't designed to work that way. Instead, you can use a plain for loop with await inside the loop. IMO, .forEach() should be ditched permanently. Regular for loops are so much more powerful these days and easier for the interpreter to optimize too. Commented Jul 10, 2020 at 5:03
  • forEach loop is not works for synchronous operations. Instead forEach you can try regular for loop as mentioned below- for(let i=0; i<Object.keys.length; i++){ if(i===email){} . . . } Use this, It will work. Commented Jul 19, 2024 at 6:02

5 Answers 5

7

Use for...of to loop over the array elements instead and you can use await.

for(const field of Object.keys(reqBody)){ //Code using await } 
Sign up to request clarification or add additional context in comments.

Comments

1

using map instead of forEach to get promises Object.keys(reqBody).map(async (field) => {...} and then using Promise. allSettled to wait for all promise resolved or rejected and you can catch that error to log

router.post('/new/user', async function (req, res, next) { const validateEmail = async (email) => { var re = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; return re.test(email); }; const reqBody = { email, password }; try{ const promises = Object.keys(reqBody).map(async (field) => { if (field === 'email' && validateEmail(await reqBody[field])) { Promise.reject({[field]: 'Not a valid Email'}) } console.log(3); if (field === 'password' && password !== '' && password < 4) { Promise.reject({[field]: 'Password too short'}) } }); // List of resolved and rejected promises const results = await Promise. allSettled(promises) const errors = results.filter(result => result.status === "rejected") if (Object.keys(results).length > 0) { res.json({ errors }); } }catch(error){ res.json({ error }); } } 

Comments

1

I don't like the syntax of awaiting within an async loop function. But if I were to keep the existing map structure, I would probably add an array outside it to hold blank promises. Push a promise into that array each time you loop, and then resolve it after your await and your pushing of the error. Then put await Promise.all on that array in between your object key loop and setting the response.

Comments

0

It's far more easy to just use a plain for loop on Object.keys(reqBody) when dealing with async stuff.

let keys = Object.keys(reqBody) for(let i in keys){ ... } 

1 Comment

You shouldn't ever use a for..in loop with arrays! Consider a for..of or, if index is needed, a regular for instead.
0

forEach loop is not works for synchronous operations. Instead forEach you can try regular for loop as mentioned below-

for(let i=0; i<Object.keys.length; i++){ if(i===email){} . . . } 

Use this, It will work.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.