JavaScript Iterations - Which One is Faster?
Loops are a fundamental part of application development. They help you repeat a task or a set of instructions several times. In JavaScript, loops validate a value in a given block of code by checking if the value meets a provided condition. <!--more--> The value is executed repeatedly until that condition is met. The condition then evaluates to false. To get a picture of how loops work in JavaScript, assume, let's say to log "hello world" six times. Here is a simple JavaScript code block to do that:
console.log("hello world")
console.log("hello world")
console.log("hello world")
console.log("hello world")
console.log("hello world")
console.log("hello world")
The code above will correctly repeat the assigned task six times. However, as the number of tasks grows, say 100 hello worlds, the code will get messy affecting code readability.
It's hard to write the same line of code 100 times. Looping through these tasks will affect execution performance drastically. Here is where JavaScript loops come to the rescue. JavaScript can control the above loop better with a few lines of codes.
For example:
for (let loop = 0; loop < 6; loop++) {
console.log("hello world")
}
With only two lines of code, we can log six "hello world" strings as we mentioned we wanted to do before. Shorter code means a faster development time, better code readability, and less performance overheads.
JavaScript has different kinds of iterations statements called loops. They include for
, while
, do while
, for in
, for of
, and for each
. They all do the same job, i.e., to repeat an action several times.
They however, have different ways to start and end a loop? When it comes to loops, the most common questions are which loop to use and which one is the fastest?
This guide will discuss each of these loops with simple & basic examples. We will also compare & test each loop's execution performance.
For Loop
For
loop is a commonly used loop. If you are a bit familiar with the world of programming, you might have come across a for
loop statement under other programming languages such as java
, c++
, c
, etc. A for
statement loops through a specified or fixed number of elements until it meets the specified condition.
A for statement
consist of the following parts:
for (initializer; condition; increment){
statement //loop body
}
- Initialization expression - it's a variable declaration that initializes a loop counter, i.e.,
loop = 0
. - Condition expression -
for
loop executes when the condition value is true, and if it is false, the loop terminates whenloop < 5
. It's a test condition that determines the number of times a statement will iterate. - Increment expression -
loop++
updates the loop by increasing or decreasing the loop counter. - Statement -
console.log ("hello world")
if the condition executes to true, the loop statement is executed until the condition expressionloop < 5
is false.
For loop example:
for (let loop = 0; loop < 5; loop++){
console.log("hello world") //loop body
}
Do While
Repeats a statement until the specified condition is evaluated to be false.
do {
statement; //loop body
} while (condition);
The statement is checked/executed before the condition. If the condition is true, the loop executes the statement again.
In a do-while
loop, the next statement executes before the condition rechecks the previously executed statement.
If the condition is true, it executes the statement again until the condition evaluates to false.
let i = 0;
do {
console.log("hello world");
i ++;
} while (i < 5);
In the example above, do
iterates before while
then i
is incremented until the condition executes to false, i.e., until the loop cycle is no longer less than five: (i < 5)
.
While Loop
It loops as long as the condition evaluates to true. When the condition evaluates to false, the loop stops.
While
checks a condition before executing the specified statement. If the statement executes, the loop retests the condition again. If the condition is false at the first loop cycle, there is a chance the loop will not call a statement.
let i = 0;
while (i < 5) {
i++;
console.log(i);
}
The above block will run until i < 5
is not true, and the loop will stop when the value of i
is 5
. If the condition is always true, the loop will never end. Thus creating an infinite loop, as we will see below:
let i = 0;
while (true) {
i++;
console.log('Hello, world!');
}
The code above block will execute forever, as the condition will never be false.
For In
It iterates over named properties of an object key and gets each property value inside the loop. A for-in
loop has no condition expression and incremental value. The loop executes once per property. Also runs the tests for every number of properties in an object.
for(variable in object) {
//statement
}
The main advantage of for-in
over the other loops is its capacity to loop through an object.
const person = {
name: "bunny",
age:" 28",
city: "LA",
maritalstatus: true
}
for(let info in person)
console.log(`${info}: ${person[info]}`)
For-in
will invoke the loop above the same number of times as the number of properties you add in the object person
. It's a better solution to loop through objects.
For Of
It repeats through iterables such as strings
, arrays
, maps
, and set
. For-of
, loops through items in a collection such as an array.
It's very similar to the for-in
, except this time we are using the of
keyword and passing in an iterable instead of an object. For-of
will not work with Objects because they are not iterable.
for(variable of iterable) {
//statement
}
For-in
also works with arrays as it uses an index for each value in the array. The following example shows the difference in the results of for-in
and for-of
to iterate over array elements.
let arr = [1, 3, 5, 7, 9];
console.log("for in");
for (let number in arr) {
console.log(number);
//0 1 2 3 4
};
console.log("for of");
for (let number of arr) {
console.log(number);
//1 3 5 7 9
};
To get the values, we would have to reference the original array and use square brackets to define the index of the value.
let arr = [1, 3, 5, 7, 9];
for (let number in arr) {
console.log(arr[number]);
//1 3 5 7 9
};
A better solution would be to use for-of
. This statement iterates a collection of elements in an array and gives the values of an array without having to refer to the original array.
let arr = [1, 3, 5, 7, 9];
console.log("for of");
for (let number of arr) {
console.log(number);
//1 3 5 7 9
};
For Each
It executes a provided function once for each array element in ascending order. When it comes to looping through arrays for-each
function is ideal, and it's simple to use.
const animals = [ "dog", "cat", "mouse", "wolf", "chicken", "rabbit"];
animals.forEach(function(anim){
console.log(anim)
})
The callback function executes the first element in the array then the second to the last element in the array. You can rewrite the function using the arrow function, as shown below.
const animals = [ "dog", "cat", "mouse", "wolf", "chicken", "rabbit"];
animals.forEach(anim => {
console.log(anim)
})
For-each
references to a callback function once. The callback function calls every single element in the array in order.
array.forEach(callback (currentValue, index, array) {
// statement
}[thisArg]);
Parameters
- Callback - the function to be called once each element are in order.
- Current value - a mandatory parameter that holds the current element value in the array.
const animals = ["dog", "cat", "mouse", "wolf", "chicken", "rabbit"];
animals.forEach((anim) => {
console.log(anim)
})
- Index - the array index of the current element array position.
const animals = ["dog", "cat", "mouse", "wolf", "chicken", "rabbit"];
animals.forEach((anim, index) => {
console.log(`index: ${index}, value: ${anim}`)
})
- Array - an optional parameter that holds a complete array from where for-each was called.
const animals = ["dog", "cat", "mouse", "wolf", "chicken", "rabbit"];
animals.forEach((anim, index, array) => {
console.log(array)
})
ThisArg
- this is an optional parameter that holds the contextthis
to be passed when executing the callback function.
For-each
's deal is to iterate over array elements without breaking the array list. It executes each array element simultaneously without side effects such as mutability of the scope variable.
It's not chainable and always returns an undefined value. It's also a high order function. If you are interested in learning more, head out to this link.
Nested Loops
A code block that has a loop inside another loop is called a nested loop.
For example:
for (let i = 0; i < 5; i++) {
for (let j = 0; j < 5; j++) {
console.log(`i = ${i}, j = ${j}`);
}
}
Loops Break and Continue
Break Statement
A break identifier terminates an iterating loop, i.e., the below examples closes a loop that iterates over array elements. The loop terminates when iteration finds an index of a specified value.
Example 1
const animals = ["dog", "cat", "mouse", "wolf", "chicken", "rabbit"];
for (let i = 0; i <animals.length; i++) {
if (animals[i]==="wolf") break;
console.log(`index:${i}, value: ${animals[i]}`)
}
The loop terminates when the index value of the animals array is equal to wolf
. The loop will stop after testing the value wolf
. This means that the loop will not print the wolf
in the console.
Example 2
Break a while
loop when the loop value is 3
.
let n = 0;
while (n < 10) {
if (n ==3 )
break;
n++;
console.log(n)
}
The while
loop will stop when the loop value is equal to 3
. Unlike For
loop, the breakpoint value 3
will be included in the log message before the iteration stops.
While
loops execute the condition (n < 10)
before executing the statement. While
loop executes a statement only when the condition is true. When the statement is invoked the while
controls the loop by retesting the condition again until n == 3
become false.
Continue Statement
A continue statement break a loop, check a specified condition and then proceed to the next iteration until the loop condition expression returns false.
The loop skips a value specified in the continue statement condition. For example, to loop over some numbers and get the even numbers, we need a continue statement to break the loop whenever we iterate odd numbers.
for (let i = 1; i <= 10; i++){
if (i % 2 == 1) continue;
console.log(i)
}
Every time i
iterates, it will check the value and if the value is an odd number, the loop will break and test the next value.
The Continue
statement will loop the current iteration and continue as long as i
is an even number until i <= 10
is met.
Note: there is no way to break out of for each
loop.
Labeled Loop Statement
A label identifies a loop. It's simply naming a loop or a block of code in JavaScript. A label statement can be added inside a loop as an identifier to interrupt the iteration execution.
These statements are called labels. These labels can then be used to refer to the code snippets later on. For instance, to add a myloop
label to a loop block, add a semicolon to the end of the label name as shown below.
myloop:
for (let i = 1; i <= 10; i++){
console.log(i);
}
The myloop
statement can be used to refer to our code later on. For example whenever a break or continue statements are needed.
myloop:
for (let i = 1; i <= 10; i++){
if (i % 2 == 1) continue myloop;
console.log(i);
}
The code above results nothing different, as the results will be the same as when no label was used.
In loops, labels come into play when using nested loops to specify the loop to be altered. The label will determine exactly which loop to break or allow to continue.
For example:
loopOne: for (let i = 0; i <=5; i++) {
loopTwo: for (let j = 0; j <=5; j++) {
if (i === 3) continue loopOne;
if (j === 3) break loopTwo;
console.log(`i = ${i}, j = ${j}`);
}
}
In the example above:
- Every loop will perform five iterations.
loopOne
will always break wheni === 3
, skip the value3
, and continue with the loop execution untili <=5
. It will log values 1, 2, 4, and 5.loopTwo
will always break and stop the iteration atj === 3
. It will log values 1, 2, and 3.
Speed Comparison
We will traverse through an array using one array method with the same arrays size and an equal number of iterations to compare each loop's execution time. To get the best results, we will use different test methods.
Using .time() and .timeEnd() Functions
//number of iterations
// use 100 iterations
const iterations = 100
// add an empty array size equal to the number of iterations
const arraySize = new Array(iterations)
console.log("Starting speed/execution test performance with arraySize iterations" , iterations);
console.log("arraySize", + arraySize.length)
//for
console.time("for")
for (let i = 0; i <iterations; i++) {}
console.timeEnd("for");
//while
console.time("while")
let i = 0;
while (i < iterations) {
i++;
}
console.timeEnd("while");
//do...while
console.time("do...while")
let ie = 0;
do {ie++;}
while (ie < iterations);
console.timeEnd("do...while");
//for...in
console.time("for...in")
for (let ele in arraySize){}
console.timeEnd("for...in");
//for...of
console.time("for...of")
for (let ele of arraySize){}
console.timeEnd("for...of");
//for...each
console.time("for...each")
arraySize.forEach(value=> "")
console.timeEnd("for...each");
Results
arraySize iterations 100
for: 0.968ms
while: 0.047ms
do...while: 0.045ms
for...of: 0.113ms
for...each: 0.065ms
The results are quite eye-catching with while
, do-while
, and for
comparatively faster. To get a clear picture and more desirable result to try to increase the number of iterations.
arraySize iterations 1000
for: 0.519ms
while: 0.129ms
do...while: 0.215ms
for...of: 1.012ms
for...each: 0.103ms
arraySize iterations 10000
for: 1.038ms
while: 1.418ms
do...while: 1.784ms
for...of: 4.314ms
for...each: 0.323ms
arraySize iterations 100000
for: 17.362ms
while: 8.722ms
do...while: 9.565ms
for...of: 29.333ms
for...each: 1.160ms
arraySize iterations 1000000
for: 17.613ms
while: 13.025ms
do...while: 13.498ms
for...of: 96.863ms
for...each: 12.207ms
arraySize iterations 10000000
for: 51.083ms
while: 42.198ms
do...while: 46.366ms
for...of: 762.958ms
for...each: 115.888ms
arraySize iterations 100000000
for: 358.317ms
while: 344.164ms
do...while: 349.297ms
for...of: 14124.834ms
for...each: 11820.696ms
As the number of iterations increases, the results become quite comparable, and again the old for
loop is giving stiff competition to the while
and do-while
loops. On the other hand, for
of loop seems to lag behind.
Nested Loop Performance
const iterations = 100000
// add an empty array size equal to the number of iterations
const arraySize = new Array(iterations)
console.log("arraySize", + arraySize.length)
//nested loop
console.time("nested loop")
for (let i = 0; i < iterations; i++) {
for (let j = 0; j < iterations; j++) {
}
}
console.timeEnd("nested loop");
Created a separate code block because the nested loop creates performance overhead as the number of iterations increases.
Test the loop up to 100000 iterations.
Results
arraySize 100: 3.554ms
arraySize 1000: 71.934ms
arraySize 10000: 490.257ms
arraySize 100000: 36745.962ms
arraySize 100000: 38987.654ms
Using Performance Observer perf_hooks
These will log several performances for every loop and then output each loop's average time to complete the assigned task.
const { PerformanceObserver, performance } = require('perf_hooks');
let arraySize = 1000000;
let iterations = 100;
console.log("Starting performance test with %d array size and %d iterations", arraySize, iterations);
let values = {
ForIn: 0,
ForOf: 0,
ForEach: 0,
For: 0,
While: 0,
DoWhile: 0
}
const obs = new PerformanceObserver((items) => {
let entry = items.getEntries()[0];
console.log(entry.name, entry.duration);
values[entry.name] += entry.duration;
performance.clearMarks();
});
obs.observe({ entryTypes: ['measure'] });
function generateArray() {
let arr = [];
for (let i = 0; i < arraySize; i++) {
arr[i] = 'val' + i;
}
return arr;
}
for (let i = 0; i < iterations; i++) {
let arr = generateArray();
//for.......each
performance.mark('A');
arr.forEach((val) => {
let x = val + 1;
});
performance.mark('B');
performance.measure('ForEach', 'A', 'B');
//For....In
performance.mark('A');
for (const val in arr) {
let x = val + 1;
}
performance.mark('B');
performance.measure('ForIn', 'A', 'B');
//For.....Of
performance.mark('A');
for (const val of arr) {
let x = val + 1;
}
performance.mark('B');
performance.measure('ForOf', 'A', 'B');
//for loop
performance.mark('A');
for (let i = 0; i < arr.length; i++) {
let x = arr[i] + 1;
}
performance.mark('B');
performance.measure('For', 'A', 'B');
//while
performance.mark('A');
let i = 0;
while (i < arr.length) {
i++;
let x = arr[i] + 1;
}
performance.mark('B');
performance.measure('While', 'A', 'B');
//do....while
performance.mark('A');
let j = 0;
do {j++;let x = arr[j] + 1;}
while (j< arr.length);
performance.mark('B');
performance.measure('DoWhile', 'A', 'B');
console.log(Object.entries(values).sort((a, b) => {
return a[1] - b[1];
}).map(obj => {
obj[1] /= iterations;
return obj;
}))};
Results
[
[ 'For', 108.80030800999994 ],
[ 'While', 110.42406587 ],
[ 'DoWhile', 110.85779098000005 ],
[ 'ForOf', 113.78390201000003 ],
[ 'ForEach', 339.78870798000025 ],
[ 'ForIn', 1214.0874350800002 ]
]
This is a more transparent result. Again the old for
loop dominates the loops list. Do-while
and while
still have comparable performance.
I included for-in
in this example. It seems to take the longest time to finish the loops. Unlike the other loops (logs the array values), for-in
logs the index of the array element.
This makes it slower. For in
is specially reserved to loops through an unspecified number of object properties.
The way you write a loop statements has a slight difference in iteration performance. For example, using the fast for
loop, we can derive the following statements.
Using Incremental
for (let i = 0; i <array.length; i++) {}
Using Decremental
for (let i = 0; i <array.length; i--) {}
Cached Length
for ( let i = 1, l = array.length; i <= l; i++ ) {}
Or
let i = 0;
const max = array.length;
for(; i < max; i++) {}
Or
let l = arr.length;
for (let x = 0; x < l; x++) {}
Var vs Let
for (let i = 0; i <array.length; i++) {}
for (var i = 0; i <array.length; i++) {}
The Ugly For Loop
for (let i=n; i-- != 0; ) {}
Function Call Loop
for ( let i = 1; i <= array.length; i++ ) {}
Other Options
for (let i=n; i >=0; i--) {}
for (let i=n; i--; ) {}
Performance Summary
const iterations = 50000
const arraySize = new Array(iterations)
console.log("arraySize", + arraySize.length)
//for ++
console.time("++")
for (let i = 0; i < arraySize.length; i++) {}
console.timeEnd("++");
// for --
console.time("--")
for (let i = arraySize.length; i <0; i--) {}
console.timeEnd("--");
//var
console.time("var ++")
for (var i = 0; i <arraySize.length; i++) {}
console.timeEnd("var ++");
//var --
console.time("var --")
for (var i = arraySize.length; i <0; i--) {}
console.timeEnd("var --");
//cached lenth
console.time("cached ++")
let j = arraySize.length;
for (let i = 0; i < j; i++) {}
console.timeEnd("cached ++");
console.time("cached --")
let k = arraySize.length;
for (let i = k; i < 0; i--) {}
console.timeEnd("cached --");
//the ugly for loop
console.time("ugly for loop ")
for (let i=arraySize.length; i-- != 0; ) {}
console.timeEnd("ugly for loop ");
//function call Loop
console.time("function call Loop ")
for ( let i = 1; i <= arraySize.length; i++ ) {}
console.timeEnd("function call Loop ");
// option 1
console.time("option 1")
for (let i= arraySize.length; i >=0; i--) {}
console.timeEnd("option 1");
//option 2
console.time("option 2")
for (let i= arraySize.length; i--; ) {}
console.timeEnd("option 2");
Results
arraySize 50000
++: 4.634ms
--: 0.038ms
var ++: 5.345ms
var --: 0.041ms
cached ++: 5.257ms
cached --: 0.044ms
ugly for loop : 6.868ms
function call Loop : 6.307ms
option 1: 3.130ms
option 2: 5.086ms
Closing Notes
For
,while
anddo-while
have close performance results. Neither of the loops is significantly faster or slower.For-each
is ideal for functional codes.For-in
is ideal for unspecified object properties. Avoid it when using array data.- Be keen on the condition statement when using
while
to perform a loop. - Nested loops are significantly slower; avoid them when a loop has a large number of iterations to perform.
- Decreasing the amount of work done per iteration and the number of loops increases loop performance.
- Performance is not the only thing that matters. Code readability and maintainability are key. Fewer lines of codes mean reduced performance overheads and a shorter development period.
- Choose a loop that fits your application model and makes sure they align with your requirements to get desirable results.
Note: The above performance results will vary depending on the browser, computer model and operating system you are using to test the loops.
Peer Review Contributions by: Linus Muema