Ultimate Guide to forEach() in JavaScript
Jasser Mark Arioste
I would like to share my knowledge about forEach function. I've seen a lot of guides out there that are incomplete so I'll try my best to cover everything in this tutorial.
forEach() Definition #
First let's start with forEach definition. Below is the forEach documentation based the typescript es5 definition: lib.es5.d.ts
interface Array<T> { ... /** * Performs the specified action for each element in an array. * @param callbackfn A function that accepts up to three arguments. * forEach calls the callbackfn function one time for each element in the array. * @param thisArg An object to which the this keyword can refer in the callbackfn function. * If thisArg is omitted, undefined is used as the this value. */ forEach(callbackfn: (value: T, index: number, array: T[]) => void, thisArg?: any): void; }
1234567891011
What does it do? #
The Array.prototype.forEach
method is one of the ways to iterate through an array in javascript. As stated in the documentation, it performs the specified action for each element in an array. It's like saying: "For each element in an array, execute this specified function." . For example let's take a look at the code below:
//we have a simple array const arr = [1,2,3,4]. //function that prints a value function callbackFunction(value){ console.log(value); } arr.forEach(callbackFunction); //output: //1 //2 //3 //in most cases, people just use an inline function or an arrow function like so: arr.forEach(function(value){ console.log(value); }) arr.forEach((value) => { console.log(value); })
1234567891011121314151617181920212223
For the example above, each value will be printed on the console.
Does forEach iterate over empty values? #
For each does not iterate over empty values but it iterates over undefined
an null
values.
//function that prints a value arr.forEach((value) => { console.log(value); }) //prints //1 //2 //3 //4 //undefined //null
123456789101112
What is the callbackfn
parameter?
#
As stated above, the callbackfn
is executed for each element in the array.
It accepts 3 arguments: value
, index
, and the array
itself. The index
and array
parameters are useful if you want to check for certain conditions when executing the action. In the example below we check if index is an even number and log the value.
The parameter values are provided by the forEach
method, we just need to specify how to use the callback for each iteration. This a very important concept in functional programming in Javascript.
//we have a simple array const arr = [1,2,3,4]. //only logs if the index is an even number arr.forEach((value, index) => { if(index %2 === 0){ console.log(value) } }) //only logs the first and last value arr.forEach((value, index, array) => { if(index === 0){ console.log(value); } if(index === array.length - 1){ console.log(value) } }) let oddNumbers = []; arr.forEach((value) => { if(value %2 === 1){ oddNumbers.push(value); } })
1234567891011121314151617181920212223242526
Does the callbackfn
return any value?
#
Based on the function definition, it does not return any value.
What happens if I use a return
statement inside the callbackfn
?
#
Just like any other function, it terminates the remaining execution of the function. For example if we can modify the example above to skip certain .
const arr = [1,2,3,4]. arr.forEach((value, index) => { if(index %2 !== 0) return; //skip if index is odd console.log(value) //print the value if index is even })
1234567
Is it possible to use async/await
in the callbackfn
?
#
It's possible to use async/away
inside the callbackfn
however we must be aware of the caveats.
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); const sleepTime = [4000, 3000, 2000, 1000]; //modify the callback function to async arr.forEach(async (value, index) => { //we can now use await await sleep(sleepTime[index]); console.log(value); }); //prints //4 //3 //2 //1
1234567891011121314
Throwing an error inside an async callback function results in an unhandled promise rejection:
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); async function test(arr) { try { arr.forEach(async (val) => { await sleep(1000) // Unhandled promise rejection because `forEach()` doesn't return // a promise, so there's no way to `await`. throw new Error('Oops!'); }); } catch (err) { // Does not execute } }
12345678910111213
Is is possible to use sequential Promise
execution inside forEach?
#
Let's say we want to call a REST api sequentially. You might think that we can use forEach but this doesn't work since forEach will execute an async callbackfn
as if it's a normal function. For example if we want to get individual todos by id sequentially, determining which promise will execute first is impossible
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); async function getTodosForEach() { let urls = [ `https://jsonplaceholder.typicode.com/todos/1`, `https://jsonplaceholder.typicode.com/todos/2`, ]; //arrow function always has empty object as this urls.forEach(async (url, index) => { const response = await fetch(url); const json = await response.json(); console.log(json); }); } document.getElementById('btn2').onclick = getTodosForEach;
123456789101112131415
For scenarios like this, it's better to use the for...of
operator to ensure sequential execution of promises:
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); async function getTodos() { let urls = [ `https://jsonplaceholder.typicode.com/todos/1`, `https://jsonplaceholder.typicode.com/todos/2`, ]; for (let url of urls) { const response = await fetch(url); const json = await response.json(); console.log(json); await sleep(1000); } } getTodos();
12345678910111213141516
The result is sequential execution:
What is the difference between using an arrow function or an anonymous function for the callbackfn
?
#
In most cases, you can use either one depending on your preference or coding guidelines. The only thing that changes is the this
operator. Using this
operator inside an arrow function is different from an anonymous function and follows the rules of the javascript this
operator.
Let's examine the differences:
//arrow function always has empty object as this arr.forEach((value) => { console.log(this) //prints {} }) //anonymous function prints Window as this. If we want to change it, we should use the optional thisArg parameter arr.forEach(function (value) { console.log(this) //prints Window }) const myObj = {prop1: "hello" } arr.forEach(function (value) { console.log(this) //prints {prop1: "hello"} }, myObj) //note that using thisArg parameter for arrow function does not work arr.forEach((value)=> { console.log(this) //it still prints {} }, myObj)
1234567891011121314151617181920
Does forEach work for Iterables like Map.keys() or HTMLCollection? #
forEach is a method for arrays and does not work for Set or Map. To iterate over Set or Map objects, we have to transform it first using Array.from() method.
const map = new Map([['key', 'value']]); // Throws "TypeError: map.keys(...).forEach is not a function" map.keys().forEach(val => console.log(val)); //correct: Array.from(map.keys()).forEach(val => console.log(val)); const elems = document.getElementsByTagName('div'); //Error: elems.forEach is not a function elems.forEach((val) => console.log(val)); //correct: Array.from(elems).forEach((val) => console.log(val));
1234567891011121314
Is it possible to use forEach from Map or Set classes? #
It is possible but Map
and Set
classes have a different forEach method from Array.forEach
Is it possible to use break
or continue
inside forEach?
#
Since callbackfn is just a plain javascript function it's not possible to use looping statements like skip or continue which will throw a runtime error
//Error: Illegal continue statement: no surrounding iteration statement let arr = [1, 2, 3, 4]; arr.forEach((value, index) => { if(index === 0{ continue; } console.log(value); }); //Error: Illegal break statement arr.forEach((value, index) => { if(index === 0){ break; } console.log(value); });
12345678910111213141516171819
An alternative to this is to use return
statement or Array.filter
method:
let arr = [1, 2, 3, 4]; //using return statement arr.forEach((value, index) => { if(index === 0){ return; } console.log(value); }); //prints //2 //3 //4 //using filter arr.filter((val,index) => index > 0).forEach(val => { console.log(val) }) //prints //2 //3 //4
12345678910111213141516171819202122
Conclusion #
With that I guess we've covered everything there is to know about Array.prototype.forEach
method, from its definition to its usage and its limitations. If there's anything lacking in this tutorial, please feel free to contact me through my email.
Thank you for reading! If you like tutorials like these please leave a like or subscribe to our newsletter to get tutorials directly to your inbox!
Resources: #
forEach documentation - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach
MasteringJS tutorial - forEach Fundamentals
Credits: Image by David Mark from Pixabay