break, continue & Labels
Loops run until their condition fails, but real-world logic rarely fits that neat shape. Sometimes you want to stop early the moment you find what you need, and sometimes you want to skip one awkward iteration and keep going. The break and continue statements give you that fine-grained control, and labels let them reach beyond the innermost loop. Used well, they make algorithms shorter and clearer; used carelessly, they hide bugs.
Breaking out of a loop
break immediately terminates the nearest enclosing loop (for, for...of, for...in, while, or do...while) and resumes execution at the statement after it. It is the classic “I found it, stop searching” tool.
const numbers = [4, 8, 15, 16, 23, 42];
let firstEven = null;
for (const n of numbers) {
if (n % 2 === 0) {
firstEven = n;
break; // stop as soon as we have an answer
}
}
console.log(firstEven);
Output:
4
Without break, the loop would keep scanning every remaining element for no reason. For very large collections that wasted work adds up, so breaking early is both clearer and faster.
Note:
breakalso exits aswitchstatement. Inside a loop that contains aswitch, abreakbelongs to theswitch, not the loop — reach for a label if you need to break the loop from inside the switch.
Skipping with continue
continue does not leave the loop; it jumps straight to the next iteration, skipping the rest of the current loop body. It is ideal for filtering out cases you don’t care about without nesting your real logic inside an if.
const orders = [
{ id: 1, status: "paid" },
{ id: 2, status: "pending" },
{ id: 3, status: "paid" },
];
for (const order of orders) {
if (order.status !== "paid") continue; // ignore unpaid orders
console.log(`Shipping order #${order.id}`);
}
Output:
Shipping order #1
Shipping order #3
In a for loop, continue still runs the update expression (the i++ part), so you won’t accidentally create an infinite loop. In a while loop, however, make sure any counter is incremented before the continue, or the condition may never change.
Labeled statements
By default break and continue only affect the innermost loop. A label — an identifier followed by a colon placed before a loop — lets you target an outer loop explicitly. This is the cleanest way to escape nested loops in one step.
const grid = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9],
];
outer: for (let row = 0; row < grid.length; row++) {
for (let col = 0; col < grid[row].length; col++) {
if (grid[row][col] === 5) {
console.log(`Found 5 at [${row}][${col}]`);
break outer; // exit BOTH loops at once
}
}
}
Output:
Found 5 at [1][1]
continue label works the same way but resumes the next iteration of the labeled loop instead of ending it:
rows: for (let i = 0; i < 3; i++) {
for (let j = 0; j < 3; j++) {
if (j === i) continue rows; // skip to the next row early
console.log(`i=${i}, j=${j}`);
}
}
Output:
i=1, j=0
i=2, j=0
i=2, j=1
| Statement | Affects | Effect |
|---|---|---|
break | nearest loop / switch | Exits the construct entirely |
continue | nearest loop | Skips to the next iteration |
break label | the labeled loop | Exits the labeled (often outer) loop |
continue label | the labeled loop | Skips to the labeled loop’s next iteration |
Readable alternatives
Labels are powerful but easy to abuse, and deeply labeled jumps can be hard to follow. Often you can express the same intent more readably.
Extracting the loop into a function lets a plain return exit every level at once — no labels required:
function findValue(grid, target) {
for (const row of grid) {
for (const cell of row) {
if (cell === target) return cell; // unwinds all loops
}
}
return null;
}
console.log(findValue([[1, 2], [3, 4]], 4));
Output:
4
For array searches, built-in methods often remove the need for manual break entirely. find, some, and findIndex all short-circuit on the first match:
const numbers = [4, 8, 15, 16, 23, 42];
const firstEven = numbers.find((n) => n % 2 === 0);
const hasBig = numbers.some((n) => n > 40);
console.log(firstEven, hasBig);
Output:
4 true
Tip: You cannot
breakorcontinueout of anArray.prototype.forEachcallback — it ignores both. If you need early exit, use afor...ofloop or switch tofind/some/every.
Best Practices
- Use
breakto stop as soon as the answer is known; avoid pointless extra iterations. - Prefer
continueover wrapping the whole loop body in anifto filter unwanted cases — it flattens nesting. - Reach for labels only for genuine nested-loop control flow, and give them descriptive names like
outerorrows. - When a labeled
break/continuemakes code hard to read, extract the loops into a function and usereturninstead. - For array lookups, prefer
find,some,every, orfindIndex, which short-circuit and express intent clearly. - Remember that
breakinside aswitchtargets theswitch, not the surrounding loop. - Never rely on
break/continueinsideforEach— they have no effect there.