# Everything you should know about JavaScript | Breaking a Loop

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:**

```js
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:**

```js
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.

```js
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.

```js
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:**

```js
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:**
```bash
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:**
```js
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](https://benchmarkjs.com/)

<img src="https://miro.medium.com/max/1400/1*Q9iKYn-x3Fsu0ohQat5Scw.png"/>


### 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?](https://blog.decipher.dev/are-for-loops-or-foreach-better-in-javascript)

```js
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.

```js
// 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.

```js
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](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/every)**: If you are familiar with **[].every()**, It is more eligant way to break a for loop in a functional way.

```js
// 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:**

```js
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:**

```js
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.

```js
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**: 
- [Loops_and_iteration](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Loops_and_iteration)
- [short-circuit-array-foreach-like-calling-break](https://stackoverflow.com/questions/2641347/short-circuit-array-foreach-like-calling-break)

