JavaScript Loops

Pick the right loop in JavaScript with for, while, for-of, for-in, and array methods.

TL;DR

  1. 01Use for and while loops for full counter control.
  2. 02Use for...of for array values and for...in for object keys.
  3. 03Use map() and filter() array methods for transformations.

Tips

  1. 01Prefer array methods like map() and filter() over for loops because they make your data transformations more readable.
  2. 02Use for...of instead of for...in on arrays because for...in iterates keys as strings and inherits prototype properties.

Warnings

  1. 01Avoid modifying an array with splice() or push() while looping, since this skips elements and causes subtle index bugs.
  2. 02Using await inside a forEach callback silently fails to pause, because forEach ignores returned promises and runs callbacks concurrently.

For and While

    for

    Runs a block of code a set number of times using an initialized counter.

    for (let i = 0; i < 5; i++) {
      console.log(i);
    }
    while

    Repeatedly executes a block of code as long as a specified condition remains true.

    let n = 0;
    while (n < 3) {
      console.log(n);
      n++;
    }
    do...while

    Runs a block of code once before checking if the loop condition is true.

    let i = 0;
    do {
      console.log(i);
      i++;
    } while (i < 3);
    break / continue

    Use break to exit a loop immediately and continue to skip the current iteration.

    for (let i = 0; i < 5; i++) {
      if (i === 2) continue;
      if (i === 4) break;
      console.log(i);
    }

For-of for Arrays

    for...of

    Iterates directly over iterable values like array elements without manual index management.

    const nums = [10, 20, 30];
    for (const n of nums) {
      console.log(n);
    }
    Iterable objects

    Works natively on strings, sets, maps, and other built-in iterable structures.

    for (const char of "Hi") {
      console.log(char);
    }
    Destructuring

    Unpacks properties directly within the loop declaration for cleaner object access.

    const users = [{ name: "Ada" }, { name: "Bob" }];
    for (const { name } of users) {
      console.log(name);
    }
    entries()

    Returns index-value pairs to retrieve the loop counter alongside the element.

    const items = ["a", "b"];
    for (const [index, val] of items.entries()) {
      console.log(index, val);
    }

For-in for Objects

    for...in

    Iterates over the enumerable string property keys of an object.

    const user = { name: "Ada", age: 30 };
    for (const key in user) {
      console.log(`${key}: ${user[key]}`);
    }
    hasOwnProperty()

    Filters out inherited properties to iterate only the object's own direct properties.

    for (const key in user) {
      if (user.hasOwnProperty(key)) {
        console.log(key);
      }
    }
    Object.entries()

    Converts object properties into key-value arrays for cleaner, modern iteration.

    const user = { name: "Ada", age: 30 };
    for (const [key, val] of Object.entries(user)) {
      console.log(`${key}: ${val}`);
    }

Async Loops

    for...of with await

    Executes async tasks sequentially, pausing the loop for each promise to resolve.

    for (const id of ids) {
      const res = await fetchItem(id);
      console.log(res);
    }
    forEach warning

    Avoid await in forEach; it ignores returned promises and fires them concurrently.

    // Broken: will not wait for save to complete
    items.forEach(async item => {
      await save(item);
    });
    Promise.all()

    Runs all async operations in parallel by mapping items to an array of promises.

    const apiCall = id => fetchItem(id);
    const results = await Promise.all(
      ids.map(apiCall)
    );
    for await...of

    Iterates over async iterables, waiting for each value to resolve sequentially.

    for await (const chunk of readStream()) {
      console.log(chunk);
    }

Nested and Control

    Nested loops

    Runs a loop inside another loop; plain break only escapes the inner loop.

    for (let i = 0; i < 2; i++) {
      for (let j = 0; j < 3; j++) {
        console.log(`${i},${j}`);
      }
    }
    Labeled break

    Exits multiple levels of nesting at once by referencing a statement label.

    outer: for (let i = 0; i < 3; i++) {
      for (let j = 0; j < 3; j++) {
        if (i === 1) break outer;
      }
    }
    Loop performance

    Cache array length before the loop to avoid querying it on every iteration.

    const len = items.length;
    for (let i = 0; i < len; i++) {
      console.log(items[i]);
    }

In Practice

FAQ