AlgoInsight

Free lesson · JavaScript Basic to Advance

break, continue, and Loop Control

Easy5 min read

Controlling Loop Flow

Loops run until their condition becomes false. But sometimes you need finer control — exit early, skip an iteration, or jump out of a nested loop. JavaScript gives you three tools for this: break, continue, and labeled statements.

// Stop the loop entirely
for (let i = 0; i < 10; i++) {
  if (i === 5) {
    break;
  }
  console.log(i);
}
// Prints: 0, 1, 2, 3, 4

// Skip just this iteration
for (let i = 0; i < 10; i++) {
  if (i % 2 === 0) {
    continue;
  }
  console.log(i);
}
// Prints: 1, 3, 5, 7, 9

These tools are simple in isolation. The interesting part is when to reach for them and when to restructure your code so you don't need them.

Interactive animation: continue and break in one walk over an array - skip one element, then abandon the rest.

break: Exit the Loop

The break keyword exits the loop immediately, skipping any remaining iterations.

const numbers = [1, 5, 3, 8, 2, 9, 4];

// Find the first number greater than 6
let found = null;
for (const num of numbers) {
  if (num > 6) {
    found = num;
    break;   // we have what we need, stop looking
  }
}

console.log(found);   // 8

break is the right tool when you've found what you came for and there's no reason to keep iterating. The classic case is searching — once you find the target, you stop.

break also works inside while and do...while loops. It does not work inside forEach, map, filter, or reduce — those methods are designed to visit every element and have no notion of early exit. That's the limitation we covered in Topic 4.4.

continue: Skip This Iteration

The continue keyword skips the rest of the current iteration and jumps to the next one. The loop doesn't stop — it just moves on.

const numbers = [1, 2, 3, 4, 5, 6, 7, 8];

for (const num of numbers) {
  if (num % 2 === 0) {
    continue;   // skip even numbers
  }
  console.log(`Processing ${num}`);
}
// Prints: Processing 1, Processing 3, Processing 5, Processing 7

continue is useful when you want to filter out certain iterations within a loop. It's an alternative to wrapping the rest of the loop body in an if block.

Compare these two styles:

const items = [1, 2, 3, 4, 5];

// Using continue
for (const item of items) {
  if (item < 3) {
    continue;
  }
  console.log(`Item: ${item}`);
}

// Using if (no continue)
for (const item of items) {
  if (item >= 3) {
    console.log(`Item: ${item}`);
  }
}

Both produce the same output. Which one reads more clearly depends on how much code is inside the loop body. For a single line, the if version is cleaner. For longer loop bodies with multiple conditions to skip, continue keeps the main logic from being deeply nested.

graph TD
    A[Start iteration] --> B[Run loop body]
    B --> C{Hit break?}
    C -->|Yes| D[Exit loop entirely]
    C -->|No| E{Hit continue?}
    E -->|Yes| F[Skip rest of this iteration]
    F --> G[Go to next iteration]
    E -->|No| H[Continue normally]
    H --> G

    style D fill:#dc2626,color:#fff
    style F fill:#f59e0b,color:#fff
    style H fill:#10b981,color:#fff
This diagram shows the control flow when break and continue are encountered inside a loop. As each iteration runs, if it hits a break statement, the entire loop exits immediately. If it hits a continue statement, the rest of the current iteration is skipped and execution jumps to the next iteration. If neither is hit, the iteration completes normally and moves on.

Nested Loops

When you have a loop inside another loop, break and continue only affect the innermost loop they appear in. This is important when working with nested iterations.

const matrix = [
  [1, 2, 3],
  [4, 5, 6],
  [7, 8, 9]
];

for (const row of matrix) {
  for (const value of row) {
    if (value === 5) {
      break;   // only breaks the inner loop
    }
    console.log(value);
  }
}
// Prints: 1, 2, 3, 4, 7, 8, 9
// The 5 was hit, broke the inner loop, then the outer loop continued with the next row

When the inner loop breaks at 5, the outer loop just moves on to the next row. We never see 5 (because of the break) but we keep iterating over the rest of the matrix.

This is usually what you want. Occasionally, you need to break out of all the loops at once — and for that, JavaScript has labeled statements.

Interactive animation: Escaping nested loops - a flag variable and two breaks, against one labeled break that exits both.

Labeled Statements (Rare but Useful)

A label is a name you give to a loop. You can then break or continue to that specific label, skipping out of inner loops to the labeled one.

const matrix = [
  [1, 2, 3],
  [4, 5, 6],
  [7, 8, 9]
];

outer: for (const row of matrix) {
  for (const value of row) {
    if (value === 5) {
      break outer;   // break out of BOTH loops
    }
    console.log(value);
  }
}
// Prints: 1, 2, 3, 4
// Found 5, broke all the way out

The label outer: names the outer loop. break outer jumps out of that loop entirely, no matter how deeply nested you are inside it.

Labeled statements are rare in real code. Many style guides discourage them because they make control flow harder to follow. The common alternative is to extract the nested loops into a function and use return to exit:

function findValue(matrix, target) {
  for (const row of matrix) {
    for (const value of row) {
      if (value === target) {
        return value;   // exits the function, naturally bypassing all loops
      }
    }
  }
  return null;
}

const matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]];
console.log(findValue(matrix, 5));

This is the modern idiomatic way to handle "break out of nested loops." The function approach is clearer than labels in almost every case. Labels exist for the rare situation where you genuinely can't restructure into a function, but reach for return first.

When break and continue Are Anti-Patterns

break and continue are powerful, but they can hide control flow. A long loop with multiple break and continue statements scattered through it is hard to follow. The logic of "when does this loop actually run all the way through, and when does it exit early?" becomes a puzzle.

Two refactors that often help:

Replace early breaks with for...of and search methods. If your loop is just looking for an element, use find, findIndex, or some instead:

const numbers = [1, 5, 3, 8, 2];

// Instead of: loop with break
let found = null;
for (const n of numbers) {
  if (n > 6) {
    found = n;
    break;
  }
}

// Use: find()
const foundCleaner = numbers.find(n => n > 6);
console.log(foundCleaner);   // 8

Replace continue with filter and forEach. If your loop is just skipping certain elements, filter the array first:

const numbers = [1, 2, 3, 4, 5];

// Instead of: loop with continue
for (const n of numbers) {
  if (n % 2 === 0) continue;
  console.log(`Odd: ${n}`);
}

// Use: filter then forEach (or for...of)
numbers
  .filter(n => n % 2 !== 0)
  .forEach(n => console.log(`Odd: ${n}`));

These refactors aren't always better. Sometimes break and continue are the right tool. But when your loop body is getting complicated, restructuring with the right array method often produces cleaner code.

Interactive animation: The early-exit methods like find, findIndex and some - what each returns and where it stops.

Common Pitfalls

Warning: The traps with break and continue.

Trying to break out of forEach. Doesn't work. forEach has no break. Use for...of instead, or restructure with find or some.

Confusing break and return. break exits a loop. return exits a function. If you're inside a function and want to stop entirely, use return. If you're just inside a loop and want to stop the loop, use break.

Overusing labels. Labeled break/continue is hard to read. Almost any code using labels can be restructured into a function with return for clarity. Reach for that first.

Why This Matters in Interviews

Interview: Loop control is rarely a direct interview topic, but it shows up in code judgment. Interviewers notice when a candidate writes a complex loop with three early breaks when a cleaner solution exists. They also notice when a candidate doesn't know about find, some, every — modern array methods that often replace complex loop control logic.

Topic 4.6 — Interview Lens: Control Flow and Iteration Patterns — covers the broader interview treatment of these decisions, including the patterns interviewers value in well-structured solutions.

Try It

Exercise: Refactor this code into something cleaner using modern array methods. The original works, but it has nested loops with break and continue that obscure what's actually happening:

function findFirstPair(numbers, target) {
  for (let i = 0; i < numbers.length; i++) {
    if (numbers[i] <= 0) continue;
    for (let j = i + 1; j < numbers.length; j++) {
      if (numbers[j] <= 0) continue;
      if (numbers[i] + numbers[j] === target) {
        return [numbers[i], numbers[j]];
      }
    }
  }
  return null;
}

Try at least two different refactors. The cleaner version may not use break or continue at all. Compare your versions — which one would you want to maintain six months from now?

Back to JavaScript Basic to Advance