Everything you should know about JavaScript | Breaking a Loop

Everything you should know about JavaScript | Breaking a Loop

Breaking a loop in JavaScript is confusing. Few tips to write a better code to understand for loops in JavaScript.

JavaScript is not only weird but confusing and ambiguous as well. The more you work and debug the more you find a new thing. The weirdness of JavaScript is one of how to break a loop. Here are a few ways to break a loop in JavaScript.

First, let’s see what are the different ways to create a loop and break a loop.

1. Traditional for, while, do-while loop

Creating a for-loop is very easy and very common.

For Loop:

function howMany(selectObject) {
  let numberSelected = 0;
  for (let i = 0; i < selectObject.options.length; i++) {
    if (selectObject.options[i].selected) {
      numberSelected++;
    }
  }
  return numberSelected;
}

Do While:

let i = 0;
do {
  i += 1;
  console.log(i);
} while (i < 5);
// While
let n = 0;
let x = 0;
while (n < 3) {
  n++;
  x += n;
}

How to break: Breakin a loop is very easy here. You can use break keyword to break a loop.

for (let i = 0; i < a.length; i++) {
  if (a[i] === theValue) {
    break;
  }
}

This is a more elegant way of breaking a loop. If you have a nested loop(loop inside the loop). You can use the labelled statement to break a loop.

function labeledLoop() {
  let x = 0;
  let z = 0;
  outer: while (true) {
    console.log("Outer loops: " + x);
    x += 1;
    z = 1;
    while (true) {
      console.log("Inner loops: " + z);
      z += 1;
      if (z === 10 && x === 10) {
        break outer;
      } else if (z === 10) {
        break;
      }
    }
  }
}

Here outer is a label given to outer-loop. Whenever the condition matches, we break the outer loop using the labeled condition.

Note: Using the labelled statement is a nice way but it is a little bit confusing for developers. It is like a goto statement that should be avoided. The same result we can get by introducing normal conditional variables.

Example:

function conditionalBreak() {
  let x = 0;
  let z = 0;
  while (true) {
    console.log("Outer loops: " + x);
    x += 1;
    z = 1;
    let breakOuterLoop = false;
    while (true) {
      console.log("Inner loops: " + z);
      z += 1;
      if (z === 10 && x === 10) {
        breakOuterLoop = true;
        break;
      } else if (z === 10) {
        break;
      }
    }
    if (breakOuterLoop) break;
  }
}

Here in the above code, We have introduced a variable breakOuterLoop and I check the value of it after each iteration of the outer loop.

Benchmark: You may say that introducing a variable and condition can slow down the code. However you may surprise when I say, The **conditionalBreak** is faster than **labeledLoop** in the benchmark.

benchmark:

labeledLoop x 9,295,537 ops/sec ±0.85% (87 runs sampled)
conditionalBreak x 9,349,901 ops/sec ±0.45% (89 runs sampled)
Fastest is conditionalBreak,labeledLoop

Test Script:

var { Benchmark } = require("benchmark");
var suite = new Benchmark.Suite();
suite.add("labeledLoop", function() {
  labeledLoop();
});
suite.add("conditionalBreak", function() {
  conditionalBreak();
});
suite.on("cycle", function(event) {
  console.log(String(event.target));
});
suite
  .on("complete", function() {
    console.log("Fastest is " + this.filter("fastest").map("name"));
  })
  .run({ async: true });

Note: I am using benchmarkjs

2. Array Functional API methods

As you know, We should try to use more Array's APIs. Array's API for looping is much cleaner and safer. However, breaking Array's methods are tricky.

If you want to read more on for-loops and understand when to use which loop. Here is an article that you can follow. Are For Loops or ForEach Better in JavaScript?

const data = [1, 2, 3, 4, 5, 6, 7];
const printValueTill5 = (v) => {
  if(v % 5 == 0) {
    break; //Error here: Illegal break statement
  }
  console.log(v)
}
data.forEach(printValueTill5)

Note: You can’t use break keyword inside a function. Either is named function of anonymous function. Same time, You can't use return false or anything like that. It is considered as returning from the given function. So the code will behave like a continued statement.

// using return
const data = [1, 2, 3, 4, 5, 6, 7];
const printValueTill5 = (v) => {
  if(v % 5 == 0) {
    return false
  }
  console.log(v)
}
data.forEach(printValueTill5)
// Output: 1 2 3 4 6 7

Solution via throwing error: You can use a throw statement to break the loop. However, it is not an elegant way.

const data = [1, 2, 3, 4, 5, 6, 7];
const BREAK_LOOP_ERROR = Symbol("BREAK_LOOP");
const printValueTill5 = v => {
  if (v % 5 == 0) {
    throw BREAK_LOOP_ERROR;
  }
  console.log(v);
};

try {
  data.forEach(printValueTill5);
} catch (e) {
  if (e !== BREAK_LOOP_ERROR) throw e;
  console.debug(e);
}

Throwing an error in the loop is an anti-pattern. Same time, If you notice how much boilerplate code we have to write every time using the throw statement.

Using Array#every: If you are familiar with [].every(), It is more eligant way to break a for loop in a functional way.

// using 
const data = [1, 2, 3, 4, 5, 6, 7];
const printValueTill5 = v => {
  if (v % 5 == 0) {
    return false; // return false to break
  }
  console.log(v);
  return true; // Else continue.
};
data.every(printValueTill5)

The method every will loop over till you return true. Once you return false, It will break the loop.

Note: Using every is one of the best ways to break loops. However, This solution has a lot of cons.

a. Every method returns a boolean either true or false at the end, So you can chain further.

Example:

const data = [1, 2, 3, 4, 5, 6, 7];
const printValueTill5 = v => {
  if (v % 5 == 0) {
    return false;
  }
  console.log(v);
  return true;
};
data.every(printValueTill5) // returns false
  .map(console.log) // Error: data.every(...).map is not a function

b. You have to change the functionality of printValueTill5 by introducing conditional return. Hard to reuse further.

c. It is hard to debug and test.

So now the question remains the same, “How do you break a loop in JavaScript?”

The best way, You can create a utility method, which is given below. It may not be as performant as the above solutions. But more clean and predictable.

tillWhen:

const _ = {};

// fn: (any[], function, () => boolean) ->  any[]

_.tillWhen = (data, fn, predicate) => {
  if (!data || !Array.isArray(data)) return data;
  let index = 0;
  let arr = [];
  do {
    arr.push(fn(data[index]));
  } while (index < data.length && !predicate(data[index++]));
  return arr;
};
module.exports = _

Uses: Using tillWhen is pretty much simple and straight forward.

const data = [1, 2, 3, 4, 5, 6, 7];
const mapValue = v => {
  return v;
};
const till5 = v => v % 3 == 0;
_.tillWhen(data, mapValue, till5)
  .forEach(x => console.log(x));
// Output: 1 2 3 4 5

Looks like we have nearly touched the surface of JavaScript's Weird Part. In the end, We have achieved what we want to achieve. If you have more suggestions. Please let me know in the comments.

Please do subscribe/follow/react to support me. Thanks.!! 🙏🙏

References:

Did you find this article valuable?

Support Deepak Vishwakarma by becoming a sponsor. Any amount is appreciated!