Compare for Loop and forEach Function in JavaScript
Tags: javascript, typescript
Published: 2020-01-04 04:42:25

Visit my Blog for the original post: [Compare for Loop and forEach Function in JavaScript](https://yizhiyue.me/2019/09/01/compare-for-loop-and-foreach-function-in-javascript/)

# Introduction

I found an interesting [discussion](https://github.com/ReactiveX/rxjs/pull/4942#discussion_r312653747) in a pull request for rxjs project.

The PR author would like to refactor this part of code

```typescript
if (_infiniteTimeWindow) {
  for (let i = 0; i < len && !subscriber.closed; i++) {
    subscriber.next(<T>_events[i]);
  }
} else {
  for (let i = 0; i < len && !subscriber.closed; i++) {
    subscriber.next((<ReplayEvent<T>>_events[i]).value);
  }
}
```

into the following format

```typescript
if (this._infiniteTimeWindow) {
  _events.forEach(event => {
    if (!subscriber.closed) {
      subscriber.next(<T>event);
    }
  });
} else {
  _events.forEach(event => {
    if (!subscriber.closed) {
      subscriber.next((<ReplayEvent<T>>event).value);
    }
  });
}
```

So basically, the author would like to refactor the `for` loop by using `forEach()` in array utility functions.

As we know, JavaScript / TypeScript has a set of powerful array utility functions including `forEach()`, `map()`, `reduce()` and so on. Those utility functions greatly improve our productivity in array operations by using functional programming paradigm and they are clearer to read and easier to understand.

I believe the above refactored codes also intend to achieve a better readability and maintainability, but the member of rxjs library rejects the changes and claim that it is

> removing optimizations that we had done on purpose

Is that really true? To prove that using a `for` loop is more efficient than `forEach()`, I did the following experiment.

# Experiment: Comparison with Simple Value Addition and Assignment

The following codes are for comparison between `for` loop and `forEach()` using an array of 20 consisting of millions items simple value addition and assignment where the execution times are printed.

```javascript
const list = Array(20000000).fill(0);
const list1 = [];
const list2 = [];

// 20 millions records with a for loop
console.time("for loop");
for (let i = 0; i < list.length; i++) {
  list1[i] = i + 1;
}
console.timeEnd("for loop");

// 20 millions records with forEach()
console.time("forEach()");
list.forEach((_, i) => {
  list2[i] = i + 1;
});
console.timeEnd("forEach()");

// Sanity check if the list1 and list2 are with the same calculation results
console.log(
  list1.every((item, index) => item === list2[index]) &&
    list1.length === list2.length
);
```

I run the codes for 5 times and get the following results.

|     | for       | forEach() |
| :-: | --------- | --------- |
|  1  | 408.801ms | 629.787ms |
|  2  | 410.799ms | 629.338ms |
|  3  | 409.691ms | 631.505ms |
|  4  | 390.361ms | 612.205ms |
|  5  | 390.503ms | 618.609ms |

So the result is very clear that `for` loop performs **better** than `forEach()`. Although there are **20 million** records, the operations are simple, only value addition and assignment. What if the operations are complexer such as demonstrated in rxjs, every subscriber is notified and certain following actions are triggered? It will become a big difference. So, when you are working on a library, it's critical to think about the performance, even if `for` loop looks "more ugly" than `forEach()`

Besides the performance, what are the other differences?

# Break Loops

If you want to exit the loop based on certain conditions, can `forEach()` do that? The answer is yes, but it's more troublesome and less straight forward compared to `for`.

In `for` loop, it's easy to break the loop by

```javascript
const list = [1, 2, 3, 4, 5];

for (let i = 0; i < list.length; i++) {
  if (list[i] > 3) {
    break;
  }
  console.log(list[i]);
}

// Only 1, 2, 3 are printed.
```

However, in `forEach()`, you cannot directly use `break` as `SyntaxError: Illegal break statement` is thrown. It's because `break` keyword can only be used in a `for` loop, while a `forEach()` accepts a **function** as a parameter and `break` keyword is **NOT** allowed in a function.

```javascript
const list = [1, 2, 3, 4, 5];

list.forEach(item => {
  if (item > 3) {
    break;
  }
  console.log(item);
});
// SyntaxError: Illegal break statement
```

You can still use a hack way to achieve the break of loop, by using a try-catch block (inspired by [HERE](https://stackoverflow.com/a/2641374)).

```javascript
const list = [1, 2, 3, 4, 5];

try {
  list.forEach(item => {
    if (item > 3) {
      throw Error();
    }
    console.log(item);
  });
} catch (e) {}

// Only 1, 2, 3 are printed
```

Or another hack way is using `some()`, because once the condition in the callback function is fulfilled, it will stop checking the rest of the items

```javascript
const list = [1, 2, 3, 4, 5];

list.some(item => {
  console.log(item);
  return item > 2; // 3 is changed to 2 because the condition checking happens after the console.log()
});
// Only 1, 2, 3 are printed
```

However, neither of the ways looks pretty as they are not straight forward nor intuitive.

1. try-catch block is meant for catching errors, but there is actually no error at all.
2. `some()` is conceptually not meant for breaking a loop, but it just happens to have the same functionality of `break`.

Therefore, it takes `extra time` for developers to figure out the outcome of these pieces of codes, which is also likely to cause careless mistakes and confusions.

# Conclusion

In this article, we have discussed the differences of `for` loop and `forEach()`.

To summarize, I would recommend to use `for` loop when

1. You have to break in the loop
2. You have a strict requirement on performance

If you don't have the above 2 conditions, it's actually better to use `forEach()` because it looks clear and concise and is better to comprehend.