JavaScript Continue Statement: Skipping Iterations
Learn how the JavaScript continue statement skips iterations in loops. Covers continue in for loops, while loops, for...of, labeled continue for nested loops, and practical filtering patterns.
The break statement exits a loop entirely. But sometimes you do not want to stop the loop. You want to skip one iteration and keep going with the next. Maybe the current item is invalid data that should not be processed. Maybe certain indices need special handling elsewhere. Maybe the current element does not match the criteria you care about.
The continue statement skips the rest of the current iteration and jumps to the next one. The loop keeps running; only the current cycle is skipped. This tutorial covers how continue behaves in every loop type, practical patterns for data filtering and validation, the critical difference in while loops versus for loops, labeled continue for nested loops, and when to use continue versus alternative approaches.
How Continue Works
The continue statement skips the remaining code in the current iteration:
for (let i = 0; i < 6; i++) {
if (i === 3) {
continue; // skip iteration when i is 3
}
console.log(i);
}
// Output: 0, 1, 2, 4, 5
// (3 is skipped, but the loop continues)Execution flow for i = 3:
i === 3is truecontinuefires- The
console.log(i)below is skipped - Execution jumps to
i++(the update expression) - Loop continues with
i = 4
| Behavior | Details |
|---|---|
| Skips | Rest of the current iteration's body |
| Continues to | Next iteration (update expression in for loops, condition check in while) |
| Loop survives | Yes, the loop keeps running |
| Works in | for, for...of, for...in, while, do...while |
| Does NOT work in | .forEach(), .map(), or other array method callbacks |
Continue in For Loops
In a standard for loop, continue jumps to the update expression (i++), not back to the condition:
for (let i = 0; i < 5; i++) {
// 1. Condition check: i < 5
if (i % 2 === 0) {
continue; // 2. Skips to step 4 (i++)
}
console.log(i); // 3. Only runs for odd numbers
// 4. Update: i++ (continue jumps here)
}
// Output: 1, 3This is safe because the counter always increments, preventing infinite loops.
Filtering Odd Numbers
const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
const evens = [];
for (let i = 0; i < numbers.length; i++) {
if (numbers[i] % 2 !== 0) {
continue; // skip odd numbers
}
evens.push(numbers[i]);
}
console.log(evens); // [2, 4, 6, 8, 10]Skipping Invalid Data
const rawData = ["42", "hello", "17", "", "99", null, "5"];
const validNumbers = [];
for (const item of rawData) {
if (item === null || item === "") continue;
const num = parseInt(item, 10);
if (isNaN(num)) continue;
validNumbers.push(num);
}
console.log(validNumbers); // [42, 17, 99, 5]Continue in While Loops: The Danger Zone
In a while loop, continue jumps to the condition check, not to an update expression. This means the counter update can be skipped:
// BUG: infinite loop!
let i = 0;
while (i < 10) {
if (i === 5) {
continue; // jumps to condition check, i stays at 5 forever
}
console.log(i);
i++;
}
// FIX: increment BEFORE the continue
let i = 0;
while (i < 10) {
if (i === 5) {
i++; // increment first
continue;
}
console.log(i);
i++;
}
// Output: 0, 1, 2, 3, 4, 6, 7, 8, 9Always Increment Before Continue in While Loops
In a for loop, continue safely jumps to the update expression. In a while loop, continue jumps directly to the condition check, bypassing any counter update at the bottom of the body. Always place the counter increment before any continue statement in while loops, or restructure the loop so the increment cannot be skipped.
Safe Pattern: Increment at the Top
let i = -1; // start one before the first value
while (i < 9) {
i++; // always runs, even after continue
if (i === 5) continue;
console.log(i);
}
// Output: 0, 1, 2, 3, 4, 6, 7, 8, 9Continue in For...Of Loops
continue works cleanly in for...of because there is no manual counter to manage:
const tasks = [
{ name: "Deploy", status: "done" },
{ name: "Test", status: "pending" },
{ name: "Review", status: "done" },
{ name: "Build", status: "pending" },
];
for (const task of tasks) {
if (task.status === "done") continue; // skip completed tasks
console.log(`TODO: ${task.name}`);
}
// TODO: Test
// TODO: BuildContinue in Do-While Loops
In a do-while loop, continue jumps to the condition check at the bottom:
let i = 0;
do {
i++;
if (i % 3 === 0) continue; // skip multiples of 3
console.log(i);
} while (i < 10);
// Output: 1, 2, 4, 5, 7, 8, 10Since the increment is before the continue, this is safe from infinite loops.
Labeled Continue: Skipping in Nested Loops
Like labeled break, labeled continue lets you skip iterations of an outer loop from inside an inner loop:
const students = [
{ name: "Alice", grades: [85, 92, 78] },
{ name: "Bob", grades: [45, 92, 88] },
{ name: "Carol", grades: [90, 95, 88] },
];
studentLoop:
for (const student of students) {
for (const grade of student.grades) {
if (grade < 50) {
console.log(`${student.name} has a failing grade (${grade}), skipping`);
continue studentLoop; // skip to the next student
}
}
console.log(`${student.name}: all grades passing`);
}
// Alice: all grades passing
// Bob has a failing grade (45), skipping
// Carol: all grades passingWithout the label, continue would only skip the current grade in the inner loop, not the entire student.
Processing a Grid with Row-Level Skipping
const grid = [
[1, 0, 3],
[4, 5, 6],
[0, 8, 9],
];
rowLoop:
for (let row = 0; row < grid.length; row++) {
for (let col = 0; col < grid[row].length; col++) {
if (grid[row][col] === 0) {
console.log(`Row ${row} contains zero, skipping entire row`);
continue rowLoop; // skip to next row
}
}
console.log(`Row ${row} is clean: [${grid[row]}]`);
}
// Row 0 contains zero, skipping entire row
// Row 1 is clean: [4,5,6]
// Row 2 contains zero, skipping entire rowContinue vs Guard Clauses
A continue statement at the top of a loop body acts like a guard clause in a function. Both approaches are valid; choose based on readability:
// Approach 1: continue as guard clause (flat structure)
for (const user of users) {
if (!user.active) continue;
if (!user.email) continue;
if (user.role === "bot") continue;
sendNewsletter(user);
}
// Approach 2: nested if (deeper structure)
for (const user of users) {
if (user.active && user.email && user.role !== "bot") {
sendNewsletter(user);
}
}| Approach | Pros | Cons |
|---|---|---|
| Continue guards | Flat code, easy to add conditions | Multiple early exits to track |
| Nested if | Single clear condition block | Deep nesting with many conditions |
Continue Guards for Complex Filtering
When you have three or more conditions to check, continue guards keep the code flat and each condition on its own line. This is easier to read and modify than a deeply nested if statement with multiple && operators.
Continue Does NOT Work in forEach
Just like break, continue is a syntax error inside .forEach():
// SYNTAX ERROR
[1, 2, 3].forEach((num) => {
if (num === 2) continue; // SyntaxError
console.log(num);
});
// Workaround: use return (acts like continue for the callback)
[1, 2, 3].forEach((num) => {
if (num === 2) return; // skips this callback invocation
console.log(num);
});
// Output: 1, 3
// Better: use for...of with real continue
for (const num of [1, 2, 3]) {
if (num === 2) continue;
console.log(num);
}Practical Patterns
Processing CSV rows with Validation
const csvRows = [
"name,age,email", // header
"Alice,28,alice@example.com",
"", // empty row
"Bob,,bob@example.com", // missing age
"Carol,35,carol@example.com",
];
const validRecords = [];
for (let i = 0; i < csvRows.length; i++) {
if (i === 0) continue; // skip header row
const row = csvRows[i].trim();
if (!row) continue; // skip empty rows
const [name, age, email] = row.split(",");
if (!name || !age || !email) continue; // skip incomplete rows
validRecords.push({ name, age: parseInt(age, 10), email });
}
console.log(validRecords);
// [{ name: "Alice", age: 28, email: "alice@example.com" },
// { name: "Carol", age: 35, email: "carol@example.com" }]Accumulating with Exceptions
const transactions = [
{ amount: 100, type: "credit" },
{ amount: -50, type: "debit" },
{ amount: 0, type: "hold" },
{ amount: 200, type: "credit" },
{ amount: -30, type: "debit" },
];
let totalCredits = 0;
for (const tx of transactions) {
if (tx.type !== "credit") continue;
totalCredits += tx.amount;
}
console.log(`Total credits: $${totalCredits}`); // Total credits: $300Batch Processing with Rate Limits
const items = getItemsToProcess();
let processed = 0;
for (const item of items) {
if (item.status === "completed") continue; // already done
if (item.locked) continue; // another process owns it
await processItem(item);
processed++;
if (processed >= 50) break; // batch limit
}Break vs Continue Comparison
const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
// continue: skip even numbers, keep looping
for (const n of numbers) {
if (n % 2 === 0) continue;
console.log(n);
}
// 1, 3, 5, 7, 9 (all odd numbers printed)
// break: stop at the first even number
for (const n of numbers) {
if (n % 2 === 0) break;
console.log(n);
}
// 1 (only first number, then 2 triggers break)| Feature | break | continue |
|---|---|---|
| Effect | Exits the entire loop | Skips current iteration only |
| Remaining iterations | All skipped | Loop continues normally |
| Use case | Found result, error, or limit reached | Current item is invalid or irrelevant |
| While loop risk | None | Can cause infinite loop if counter is skipped |
Best Practices
Place continue guards at the top of the loop body. Early continues act as guard clauses, keeping the main logic unindented and easy to read. Avoid continue statements buried in the middle of complex logic.
Always increment before continue in while loops. In for loops, continue safely jumps to the update expression. In while loops, it jumps to the condition, so any counter update after the continue is skipped. Put the increment before the continue or at the very top of the body.
Prefer for...of when using continue. The for...of loop has no manual counter to manage, eliminating the risk of infinite loops from skipped updates.
Use continue for filtering, not complex logic branching. Continue works best for simple "skip this one" decisions. If the skip logic requires multiple steps or side effects, extract it into a function.
Limit to one or two continue conditions per loop. More than two or three continue guards at the top of a loop can be hard to track. If the filtering logic is complex, consider using .filter() before the loop to separate concerns.
Rune AI
Key Insights
- Continue skips one iteration, not the whole loop: the loop keeps running with the next item
- Safe in for loops: continue jumps to the update expression, so the counter always increments
- Dangerous in while loops: counter updates after continue are skipped, risking infinite loops
- Use as guard clauses: place continue checks at the top of the loop body to keep main logic flat
- Does not work in forEach: use
returninside callbacks or switch tofor...offor proper continue support
Frequently Asked Questions
What is the difference between continue and break?
Does continue skip the update expression in a for loop?
Can I use continue in a forEach loop?
Can continue cause an infinite loop?
When should I use continue versus filter?
Conclusion
The continue statement skips the current iteration and moves to the next one, keeping the loop alive. It is most useful as a guard clause at the top of a loop body, filtering out invalid or irrelevant items before the main processing logic. The critical safety rule is to always place counter updates before continue in while loops, since continue jumps directly to the condition check and can bypass updates. For cleanest behavior, use for...of loops where there is no manual counter to manage.
More in this topic
OffscreenCanvas API in JS for UI Performance
Master the OffscreenCanvas API to offload rendering from the main thread. Covers worker-based 2D and WebGL rendering, animation loops inside workers, bitmap transfer, double buffering, chart rendering pipelines, image processing, and performance measurement strategies.
Advanced Web Workers for High Performance JS
Master Web Workers for truly parallel JavaScript execution. Covers dedicated and shared workers, structured cloning, transferable objects, SharedArrayBuffer with Atomics, worker pools, task scheduling, Comlink RPC patterns, module workers, and performance profiling strategies.
JavaScript Macros and Abstract Code Generation
Master JavaScript code generation techniques for compile-time and runtime metaprogramming. Covers AST manipulation, Babel plugin authorship, tagged template literals as macros, code generation pipelines, source-to-source transformation, compile-time evaluation, and safe eval alternatives.