Why we cant use await inside forEach? and its workaround.
Tags: javascript, beginners, programming, tutorial
Published: 2024-01-13 20:39:36

Have you ever tried using the await inside forEach? 
```js
[].forEach(async(item) => {
    await doSomethingWith(item)
})
```

Well I have, when I didn't know much about async/await and forEach. But didn't have the expected result. To understand this, we must first understand how async/await works.

### How async/await works:
`async/await` is the another way of doing `.then` on the Promises. So whenever an async function reaches the statement where await is used and Promise is yet to be resovled, that function is then runs separately from the parent function, unless we use await on the child function. Let me give you an example: 
```js
const functionA = async () => {
  console.log("Starting Function A");
  await new Promise((resolve) => {
    setTimeout(resolve, 300);
  });
  console.log("Ending Function A");
};

const myFunction = () => {
  console.log("1");
  functionA();
  console.log("2");
};
myFunction();
```

The above code logs:
```
1
Starting Function A
2
Ending Function A
```

You see, first `1` is logged, and then we go inside functionA, logs `Starting Function A` and as soon as we hit `await` keyword, `functionA` runs separately and `myFunction` moves on and logs `2` and finally when our promise resolves in 300 ms, we get the log of `Ending Function A`.

Lets give it another try with using await on functionA:
```js
const functionA = async () => {
  console.log("Starting Function A");
  await new Promise((resolve) => {
    setTimeout(resolve, 300);
  });
  console.log("Ending Function A");
};

const myFunction = async () => {
  console.log("1");
  await functionA();
  console.log("2");
};
myFunction();
```

Now we get the logs: 
```
1
Starting Function A
Ending Function A
2
```

In this case, we must complete the execution of functionA to move on. But you notice that out `myFunction` also becomes async. So it will also runs separately from parent function or global scope.

Let's log another thing just after calling `myFunction`:
```js
const functionA = async () => {
  console.log("Starting Function A");
  await new Promise((resolve) => {
    setTimeout(resolve, 300);
  });
  console.log("Ending Function A");
};

const myFunction = async () => {
  console.log("1");
  await functionA();
  console.log("2");
};
myFunction();
console.log("3");
```

Can you guess what will be logged?
Here is our logs: 
```
1
Starting Function A
3
Ending Function A
2
```

You see, our code runs synchronously until we hit the await statement where Promise is yet to be resolved. So when we called `myFunction`, our code ran synchronously and logged `1` and then it went inside the functionA, here it logged `Starting Function A`, and then we hit the await statement where we have to wait for the Promise to resolve, so both of our functions, `functionA` and `myFunction` runs separately. And our code pointer comes to log `3`. And as soon as promise resolves, it logs `Ending Function A` and then `2`


Now it might occur to you why we cannot use `async/await` inside forEach.
Let's see the forEach function first:
```js
const forEach = (callback)=>{
    ...
    ...
    callback()
    ...
    ...
}
```

Now when we do: 
```js
myArray.forEach(async(item) => {
    await doSomethingWith(item)
})
```

As soon as our code hit await statement, our `doSomethingWith` function runs asynchoronously and our code pointer comes back execute the next line in `forEach` and hence, it completes the whole forEach and our await statements are still yet to be resolved.

Let's see an example here:
```js
const promiseFunction = async (item) => {
  await new Promise((resolve) => {
    setTimeout(resolve, 600);
  });
};

const myArray = [1, 2, 3, 4, 5, 6, 7, 8];

const myFunction = () => {
  console.log("Starting my Function");
  myArray.forEach(async (item) => {
    console.log("Starting forEach with item value", item);
    await promiseFunction(item);
    console.log("Ending forEach with item value", item);
  });
  console.log("Ending my Function");
};

myFunction();
```

And here is our logs:
```
Starting my Function
Starting forEach with item value 1
Starting forEach with item value 2
Starting forEach with item value 3
Starting forEach with item value 4
Starting forEach with item value 5
Starting forEach with item value 6
Starting forEach with item value 7
Starting forEach with item value 8
Ending my Function
Ending forEach with item value 1
Ending forEach with item value 2
Ending forEach with item value 3
Ending forEach with item value 4
Ending forEach with item value 5
Ending forEach with item value 6
Ending forEach with item value 7
Ending forEach with item value 8
```

You see it starts the next callback without ending the previous, that is because each callback is waiting for its await to be resolved. Hence we get to the end of the `myFunction` but could not complete callback functions.

### The workaround:
We can use await Promise.all with map function to have await for each of the callbacks. Here is how:
```js
const promiseFunction = async (item) => {
  console.log("Starting promiseFunction with item value", item);
  await new Promise((resolve) => {
    setTimeout(resolve, 600);
  });
  console.log("Ending promiseFunction with item value", item);
};

const myArray = [1, 2, 3, 4, 5, 6, 7, 8];

const myFunction = async () => {
  console.log("Starting my Function");
  await Promise.all(
    myArray.map((item) => {
      return promiseFunction(item);
    })
  );
  console.log("Ending my Function");
};

myFunction();

```

We are simply making the array of Promises by:
```js
myArray.map((item) => {
  return promiseFunction(item);
})
```

`promiseFunction` returns a function so by `map` funtion, we have array of Promises. And we finally resolve all the promises by: `await Promise.all`.


And here is our logs:
```
Starting my Function
Starting promiseFunction with item value 1
Starting promiseFunction with item value 2
Starting promiseFunction with item value 3
Starting promiseFunction with item value 4
Starting promiseFunction with item value 5
Starting promiseFunction with item value 6
Starting promiseFunction with item value 7
Starting promiseFunction with item value 8
Ending promiseFunction with item value 1
Ending promiseFunction with item value 2
Ending promiseFunction with item value 3
Ending promiseFunction with item value 4
Ending promiseFunction with item value 5
Ending promiseFunction with item value 6
Ending promiseFunction with item value 7
Ending promiseFunction with item value 8
Ending my Function
```

Now we can see, although each `promiseFunction` runs asynchronously, but we ensure the our `myFunction` ends only when our all the Promises get resolved.

Hope you learned something today.
Happy Learning ❤️