1

I am learning node.js and I am confused about how async works. My understanding is that Array.foreach is blocking but then the below piece of code does not work as expected.

testfunc.ts:

export const testfunc = async (numArray: number[]) => {
  let count = 0;
  numArray.forEach(async (elem) => {
    if (elem % 2 === 0) {
      await new Promise((r) => setTimeout(r, 2000));
      console.log(elem);
      count = count + 1;
    }
  });
  console.log(`printed ${count}`);
};

sendmessage.ts:

import { testfunc } from "./testfunc";

const callTestFunc = async () => {
  await testfunc([1, 2, 3, 4, 5, 6]);
};

callTestFunc();

I was expecting an out put of:

2
4
6
printed 3

But, I am getting

printed 0
2
4
6

Can someone explain this to me.

VLAZ
  • 26,331
  • 9
  • 49
  • 67
Ahtesham Akhtar
  • 203
  • 2
  • 13
  • 1
    `forEach` doesn't know that the callback function is asynchronous. It calls the function for every element of the array and ignores its return value. *That* process (iterating over the array) is synchronous. The callback function schedules asynchronous work, which is guaranteed to be executed *after* you iterated over the array. – Felix Kling Mar 17 '22 at 10:50
  • @FelixKling, based on your comment I understand that foreach is not bothered about waiting for the callback function to return. It just calls the callback function and then moves on to the next iteration. In that case how can I get the correct count for the count variable. Basically, I want to know how many times was if condition satisfied. – Ahtesham Akhtar Mar 17 '22 at 12:24
  • I just posted an answer and deleted it because it's pretty much a dupe of https://stackoverflow.com/a/37576787/568458 - the only other thing to understand is that `async` is basically a shorthand for a function that returns a `Promise` wrapped around the function. Your forEach is syncronous, but all it does syncronously is create a bunch of `Promise`s which queue their inner functions to run next on the event loop. So it syncronously puts the inner functions in the queue, continues, then when it's done, that's when the event loop moves to the next task. – user56reinstatemonica8 Mar 17 '22 at 12:24
  • 1
    So "foreach is not bothered about waiting for the callback function to return" is not quite right, it always waits for the callback functions to return, but `async function () { something }` just wraps `something` in a promise, queues it up, then immediately returns that promise; if you want to wait for the inner function to complete you have to catch those promises somehow and act when they **resolve**, like `await Promise.all(numArray.map(async (elem) => { ...` (where the `map` catches all the promises and returns them in an array) – user56reinstatemonica8 Mar 17 '22 at 12:31
  • Thank you, guys. I have a better understanding of how this is working now. – Ahtesham Akhtar Mar 18 '22 at 18:32

1 Answers1

-3

With forEach, you do your stuff in a callback function and this callback is taking some times. So it's asynchronous.

Consequently, forEach can't be used for synchronous tasks.

This is also the same for a for loop which is calling a function which is asynchronous. For example, if you execute a SQL INSERT query inside a for loop, you can not be sure that INSERT are executed in the loop order.

Alaindeseine
  • 3,260
  • 1
  • 11
  • 21
  • 1
    That's... incorrect. `forEach` is synchronous and unaware that its callback might kick off asynchronous tasks. – AKX Mar 17 '22 at 11:05
  • @AKX You have misread what i wrote! I never wrote that foreach iterate asynchronously. I just explain that instructions in callback may be executed in another order due to what they do. – Alaindeseine Mar 17 '22 at 12:15