Before you begin
Why this matters
continue, break, and return each end something early: one pass, one loop, or the whole function. Mixing them up gives loops that skip work, stop too late, or stop too much, and the program still runs without an error. Knowing where each one sends the program lets you predict which line runs next.
What this helps you reason about
- Predict which line runs after continue, break, or return, also in a loop inside another loop.
- Spot a break that leaves only a switch or only the inner loop, and a continue that skips an update.
- Leave an outer loop with a label, and use early returns to keep a function flat.
A pumpkin field has rows of pumpkins to weigh, rotten ones to skip, and a prize pumpkin to find. A loop that walks through them often has to stop early: skip one pumpkin, end the search, or leave with the answer. This lesson shows where a TypeScript program goes next after continue, break, and return.
Already know this? The short version
- continue ends the pass; in for (…; …; i++) the update still runs, but an update written below continue in the body is skipped.
- break leaves only the innermost loop or switch. search: for (…) with break search or continue search acts on the outer loop.
- Inside a switch in a loop, break ends the switch and the loop goes on; leave the loop with a labeled break or return.
- return ends the function from any depth, with every loop in it; the lines after the loops run only when nothing returned. Guard clauses return early so the ordinary case is not nested.
Words in this lesson (6)
- continue
- A statement that ends the current pass of a loop at once. A for loop then runs its update; every loop then tests its condition again.
- break
- A statement that ends the innermost switch or loop it is in at once; the program continues after it.
- nested loop
- A loop written inside the body of another loop. The inner loop runs all its passes during each pass of the outer loop.
- loop label
- A name and a colon written in front of a loop, such as search:. break search and continue search then act on that loop instead of the innermost one.
- early return
- A return that ends the call before the function’s last line, usually inside an if. The lines after it do not run in that call.
- guard clause
- An if at the top of a function or method, or of a loop body, that returns or continues at once for a special case, so the code below it handles only the ordinary case.
Examples are focused snippets; imports, enclosing functions, or classes may be omitted. C examples use C11; Haskell examples use Haskell 2010. Expand the notes for language-specific depth.
01 / Continue
Where does continue send the program?
A loop runs its body once for each pass. Three statements can end something early, and each one sends the program to a different place: continue ends one pass, break ends a loop, and return ends the whole function. This lesson follows each one.
continue ends the current pass at once. The rest of the body is skipped, and the loop goes on with its next pass. In a counting for loop, the update, i++, is part of the for line, not of the body, so it still runs before the next test: i++ adds 1 to i. continue cannot skip it.
Below, a rotten pumpkin weighs 0 and is not for sale. For pumpkins 1 and 3, continue skips the rest of the body, so their weight is never added, and the loop goes on with the next pumpkin.
A line in the body that changes a variable is different: when it comes after the continue, that pass skips it. In a while loop whose body changes what the condition reads, that can make a loop that never ends, as While loops and termination showed. Put an update that every pass needs above the first continue.
let weights = [4, 0, 7, 0, 5];
let total = 0;
for (let i = 0; i < weights.length; i++) {
if (weights[i] === 0) {
continue; // pumpkins 1 and 3
}
total += weights[i];
}
console.log(total); // 16 continue ends the current pass and goes on with the next one. The loop’s own update still runs, but the lines below continue in the body do not.
Experiment
Skip the rotten pumpkins
continue ends the pass at once. For a rotten pumpkin, the lines below it, the += and the print, are skipped, and the loop goes on with its next pass. The update i++ is part of the for line, not of the body, so it still runs before the next test. break ends the whole loop instead, so the program goes to the line after the loop, and pumpkins 2, 3, and 4 are never looked at.
Why it matters Read continue as “go on with the next pass” and break as “leave the loop”. On that pass, every line below either one in the body is skipped.
Predict the lines printed and the total. Then change continue to break and predict again.
let weights = [4, 0, 7, 0, 5];
let total = 0;
for (let i = 0; i < weights.length; i++) {
if (weights[i] === 0) {
continue; // rotten: not for sale
}
total += weights[i];
console.log("pumpkin " + i + " weighs " + weights[i] + " kg");
}
console.log("total " + total + " kg");
What to look for
Before a let line has run, its variable shows not initialized yet. At pumpkin 1, the stepper goes from the if line to the continue line, then back to the for line, first with i 1 and then with i 2: the update i++ still ran. This debugger sometimes stops on the same line twice; that is normal. The run prints pumpkin 0 weighs 4 kg, pumpkin 2 weighs 7 kg, pumpkin 4 weighs 5 kg, then total 16 kg. After the edit, the stop after pumpkin 1’s break is the last console.log line, and the run prints pumpkin 0 weighs 4 kg, then total 4 kg.
Expected output
pumpkin 0 weighs 4 kg
pumpkin 2 weighs 7 kg
pumpkin 4 weighs 5 kg
total 16 kg02 / Break
Which loop does break leave?
break ends the loop at once, and the program continues with the line after the loop. A loop written inside the body of another loop is a nested loop. There, break leaves only the innermost loop: the one whose body it is written in. The outer loop goes on with its next pass.
rows below is an array of arrays: rows[1] is the second row, and rows[1][0] is the first pumpkin in it. The search below looks for the first prize pumpkin, one over 10 kg, row by row. With a plain break, the break after 12 would leave row 0 but not the search: the outer loop would go on to row 1, find 11, and prize would end as 11. The code below leaves both loops instead.
To leave both loops, give the outer loop a label: a name and a colon written in front of it, such as search:. Then break search leaves the loop named search, and continue search goes on with that loop’s next pass. A label changes nothing else about the loop.
let rows = [[4, 12, 6], [11, 3, 8]];
let prize = 0;
search: for (let r = 0; r < rows.length; r++) {
for (let p = 0; p < rows[r].length; p++) {
if (rows[r][p] > 10) {
prize = rows[r][p];
break search; // plain break: prize 11
}
}
}
console.log(prize); // 12 break leaves only the innermost loop. To leave an outer loop, give it a label and break with that label.
Coming from Python: Python has no labeled break. JavaScript has one: write a label in front of the loop, then break label.
Experiment
Find the first prize pumpkin
break ends only the innermost loop, the one whose body it is in: here, the loop over the pumpkins of one row. The outer loop is not affected. It goes on with row 1, where 11 is the first pumpkin over 10 kg, so prize is overwritten. break search names the outer loop, so it ends both loops at once.
Why it matters In a loop inside a loop, decide which loop an exit must leave. A plain break leaves only the inner one.
Predict which pumpkins are checked and which prize is printed. Then write search: in front of the outer for, change break; to break search;, and predict again.
let rows = [[4, 12, 6], [11, 3, 8]];
let prize = 0;
for (let r = 0; r < rows.length; r++) {
for (let p = 0; p < rows[r].length; p++) {
console.log("check " + rows[r][p]);
if (rows[r][p] > 10) {
prize = rows[r][p];
break;
}
}
}
console.log("prize " + prize + " kg");
What to look for
After prize = rows[r][p] sets prize to 12, the stepper stops on break, and then on the outer for line: r becomes 1, and the inner loop starts again. At the start of row 1, p can still show 1, its value from row 0, until the inner for line sets it to 0. The run prints check 4, check 12, check 11, then prize 11 kg. After the edit, the stop after break search is the last console.log line, and the run prints check 4, check 12, then prize 12 kg.
Expected output
check 4
check 12
check 11
prize 11 kg03 / Inside a switch
What does break leave inside a switch?
A switch inside a loop gives break two possible meanings. Branches and pattern matching used break to end a case. Inside a switch, break always ends the switch, even when the switch is inside a loop, and the loop goes on with its next pass.
Below, -1 means the stall closes. With a plain break under case -1, only the switch would end: the loop would go on, and the 6 kg after the closing would still be sold, 20 kg instead of 14. To leave the loop from inside the switch, label the loop and write break with the label, or return from the function. continue does not belong to a switch, so continue inside a switch goes on with the loop’s next pass.
let crates = [5, 9, -1, 6];
let sold = 0;
day: for (let i = 0; i < crates.length; i++) {
switch (crates[i]) {
case -1:
break day; // plain break: sold 20
default:
sold += crates[i];
}
}
console.log(sold); // 14 Inside a switch, break ends the switch, not the loop around it. To leave the loop, break with the loop’s label.
Coming from Rust: In a Rust match, break leaves the loop. In a JavaScript switch, it leaves only the switch.
Experiment
Close the stall from inside a switch
Inside a switch, break ends the switch, even when the switch is inside a loop. The program continues after the switch, which is the end of the loop body, so the loop goes on with the next crate, and 6 kg is sold after the stall has closed. break day names the loop, so it ends the loop.
Why it matters Inside a switch, break leaves the switch. To leave the loop from there, break with the loop’s label, or return.
Predict the kilograms sold. Then write day: in front of the for, change break; to break day;, and predict again.
let crates = [5, 9, -1, 6]; // -1: the stall closes
let sold = 0;
for (let i = 0; i < crates.length; i++) {
switch (crates[i]) {
case -1:
console.log("stall closes");
break;
default:
sold += crates[i];
}
}
console.log("sold " + sold + " kg");
What to look for
At crate 2, the stepper goes from the switch line to the console.log line under case -1, then to break, and the next stop is the for line, where i becomes 3: the loop went on. The run prints stall closes, then sold 20 kg. After the edit, the stop after break day is the last console.log line, and the run prints stall closes, then sold 14 kg.
Expected output
stall closes
sold 20 kg04 / Return
What does return leave?
return ends the whole function call at once, from any depth: inside a loop, inside a loop within a loop, or inside a switch. Every loop around it ends too, the lines after the loops in that function do not run, and the program goes back to the caller with the value after return, as From source to a running program showed.
Below, findPrize returns 12 as soon as it finds it. Row 1 is never searched, and "no prize pumpkin" is never printed. That line runs only when both loops finish without finding a prize, so it handles the case where nothing is found.
So a search inside nested loops can be written as a function that returns the answer where it finds it. The return ends both loops, with no label needed.
function findPrize(): number {
let rows = [[4, 12, 6], [11, 3, 8]];
for (let r = 0; r < rows.length; r++) {
for (let p = 0; p < rows[r].length; p++) {
if (rows[r][p] > 10) {
return rows[r][p]; // ends both loops
}
}
}
console.log("no prize pumpkin");
return 0;
} return ends the whole function, with every loop inside it. The lines after the loops run only when no return was reached.
Experiment
Return from inside two loops
return ends the call of findPrize at once, wherever it is written. Both loops end with it, the lines after the loops are skipped, and the program goes back to the caller with 12. After the edit, no return runs inside the loops, so both loops finish, and the lines after them run.
Why it matters A return inside loops ends the whole function. A line after the loops runs only when no return was reached.
Predict what is printed. Then change 10 to 20, so that no pumpkin is heavy enough, and predict again.
function findPrize(): number {
let rows = [[4, 12, 6], [11, 3, 8]];
for (let r = 0; r < rows.length; r++) {
for (let p = 0; p < rows[r].length; p++) {
if (rows[r][p] > 10) {
return rows[r][p];
}
}
}
console.log("no prize pumpkin");
return 0;
}
console.log("prize " + findPrize() + " kg");
What to look for
While the loops run, the frame list shows findPrize above <top level>. At the return line, r is 0 and p is 1. The next stop is the last line, in <top level>, and the findPrize frame is gone. The stepper never stops on the "no prize pumpkin" line. The run prints prize 12 kg. After the edit, both loops run to the end, the stepper stops on the "no prize pumpkin" line and on return 0, then back in <top level>, and the run prints no prize pumpkin, then prize 0 kg.
Expected output
prize 12 kg05 / Guard clauses
When should a function return early?
Returning early also helps outside loops. A guard clause is an if at the top of a function that returns at once for a special case. Each special case is handled and finished at the top, and the ordinary case is written last, not nested inside else blocks. Without early returns, each rule wraps the next one in an else block, and the ordinary case ends up nested deepest.
Below, a stall prices pumpkins per kilogram. weight is a parameter: the value the caller writes in the parentheses, such as 12 in pricePerKg(12). Function calls covers parameters. The two guard clauses handle a rotten pumpkin and a prize pumpkin, and the last line is the ordinary price.
Inside a loop, continue works as a guard clause for one pass. if (weights[i] === 0) continue; at the top of the body handles the rotten pumpkin, and the rest of the body is written for a pumpkin that is for sale.
function pricePerKg(weight: number): number {
if (weight <= 0) {
return 0; // rotten or missing: not for sale
}
if (weight > 10) {
return 3; // a prize pumpkin
}
return 2; // the ordinary case
}
console.log(pricePerKg(0), pricePerKg(12), pricePerKg(5)); // 0 3 2 A guard clause returns early for a special case at the top of a function, so the ordinary case is written last and is not nested.
06 / Try it
Can you explain a fresh example?
Follow each exit to the place it sends the program, and note which loop each break leaves. The next lesson, Function calls, shows what happens when a function is called.
let rows = [[2, 0, 3], [0, -1, 9], [5, 4, -1]];
let total = 0;
for (let r = 0; r < rows.length; r++) {
for (let p = 0; p < rows[r].length; p++) {
if (rows[r][p] === 0) {
continue;
}
if (rows[r][p] < 0) {
break;
}
total += rows[r][p];
}
}
console.log("total " + total);
What is printed, and which positive number is never added?
Reveal the reasoning
total 14. In row 0, continue skips the 0, and 2 and 3 are added. In row 1, continue skips the 0, and break at -1 leaves row 1’s loop before 9 is read. The outer loop goes on: row 2 adds 5 and 4, and the break at -1 leaves that row too. Each break left only the inner loop, so 9 is the only positive number never added. If break had ended both loops, the total would be 5.
Practice tickets
- BREAK-101 · Butterflies from the next walk are added to today’s count → Free
- BREAK-102 · Garden path lamps are counted as reachable when they are not → Sign in · Pro
- BREAK-103 · The orchard app says the picker is back at the shed too early → Sign in · Pro