Test HTTP Requests Tools Blog Learn Quizzes Smile API Log In / Sign Up
Test HTTP Requests Tools Blog Learn Quizzes Smile API Log In / Sign Up
« Return to the tutorials list
We have updated the website and our policies to make sure your privacy rights and security are respected.
Click here to learn more about the way our website handles your data.

Remove this message.

3 JavaScript Loop Gotchas

Difficulty: 30 / 50 Tweet
plane-doing-a-loop-stunt

A while ago I wrote this article called 3 php loop gotchas. This is its JavaScript counterpart.

forEach is unbreakable

You can not break out of a forEach loop when going through an Array. Don't try to return false or break because it won't work.

Array.prototype.forEach receives a callback that is applied to every element in the Array you're trying to iterate over and often you'll want to exit the loop once a certain condition is met.

Let's start with something to loop over:

let a = [
    {"name": "tomato", "color": "red"},
    {"name": "banana", "color": "yellow"},
    {"name": "tangerine", "color": "orange"},
    {"name": "lemon", "color": "yellow"},
];

The break statement below will throw an error: Uncaught SyntaxError: Illegal break statement:

a.forEach((item, key) => {
    console.log(item.name);
    if(item.color == 'orange') {
        break;
    }
})

The return statement below only exits the callback function. So the forEach loop will move to the next item in the Array.

a.forEach((item, key) => {
    console.log(item.name);
    if(item.color == 'orange') {
        return;
    }
})

There are several ways one could get around this: using Array.prototype.some, wrapping everything in a try catch or simply using a for loop.

Here's how each of the above methods would work.

Since the exception is caught, we achieve the same effect as if we would use a break statement in a regular loop.

try {
    a.forEach((item, key) => {
        console.log(item.name);
        if(item.color == 'orange') {
            throw "break"; // this will throw an error: Uncaught SyntaxError: Illegal break statement
        }
    })
} catch (e) { }

Using Array.prototype.some a function that is meant to test if at least one element of an array meets a certain condition. The function will continue looping until the condition is true.

a.some((item, key) => {
    console.log(item.name);
    return (item.color == 'orange'); //the loop will stop when this is true
})

A basic for loop saves us from dealing with language specific issues. If, like me you're wondering why you can't just use break or return false in a forEach loop, then just go the classic route.

let count = a.length;
for(let i=0; i<count; i++) {
    console.log(a[i].name);
    if(a[i].color == 'orange') {
        break;
    }
}

Closures inside loops ? Be carful or use let

When creating a closure inside a loop, the index being referenced in your closures (if defined with var) will always point to the same variable. In other words, the last value of the looped variable will always be used. To get around this, you either move the closure code in an external function or use let.

Let's look at the code:

var arrayOfFunctions = [];
for(var i = 0; i < 10; i++){
    arrayOfFunctions[i] = function(){  return i; };
}

console.log(arrayOfFunctions[1]()) //this will be 10... not 9, not 1 ... 10
console.log(arrayOfFunctions[5]()) //this will be 10... not 9, not 5 ... 10

The function calls above will return 10 because each of the closure functions return the value of i and since the call is done after the for loop finishes, the last value of i is 10, and that's what our closures will return.

In EcmaScript 6 we now have the let keyword which limits the scope of a variable to the block it was defined in. This means that each closure will reference the value of i that existed at the time when it was created.

var arrayOfFunctions = [];
for(let i = 0; i < 10; i++){
    arrayOfFunctions[i] = function(){  return i; };
}

console.log(arrayOfFunctions[1]()) // 1
console.log(arrayOfFunctions[5]()) // 5

If you're coding for IE or some other engine that doesn't support ES6, here's a workaround: Create an external function which will receive i as a parameter and thus create a new scope for each value of i. Then, inside the function return a closure and thus i will be the intended value.

function fn(i) { // this creates a new scope for each i
    return function() {
        return i;
    }
}
for(var i = 0; i < 10; i++){
    arrayOfFunctions[i] = fn(i)
}

console.log(arrayOfFunctions[1]()) // 1
console.log(arrayOfFunctions[5]()) // 5

JavaScript has do/while and it executes at least once

Even if it's a less used construct, do/while exists in JavaScript. One thing that's important to remember (and this is valid for all languages) do/while loops execute at least once since the execution of the loop iteration comes first and then comes the evaluation of the condition to decide if the subsequent iteration should be executed or not.

let a = 0;
do { 
    a++; //this executes first
} while (false) // this breaks the execution - so the second iteration doesn't happen

console.log(a); // this is 1

Hope you enjoyed this and that it helped you.

comments powered by Disqus