Before you begin
Why this matters
A loop runs its body again and again, and one small mistake in its condition or its body decides whether it stops at the right place, one pass early, or never. Knowing how the test and the body take turns lets you predict the number of passes and the values left after a loop.
What this helps you reason about
- Predict how many times a loop runs, and what its variable holds afterwards.
- Spot a loop that never stops because its body changes the wrong thing or skips the change.
- Write a loop condition that stops at the end of an array, and one that does not depend on exact decimal values.
An office lift goes up, stops at floors, and opens its door, one step at a time. A while loop lets a program repeat a step until something is true. This lesson shows when a TypeScript loop tests its condition, what must change for it to stop, and what its variable holds afterwards.
Already know this? The short version
- while (condition) { body } tests the condition before each pass; when it is false, even at the start, the loop ends and the program continues after it.
- Each pass must change something the condition reads, toward false. A body that changes another variable, or a continue that skips the update, gives an infinite loop.
- After the loop, the variable holds the first value that failed the test: 6 after while (floor < 6) counting up from 2, and 7 with <=. Check the last value that should run and the first that should not.
- do { body } while (condition); runs the body at least once; i < list.length && list[i] … puts the guard first; and a loop on 0.1 steps must count whole steps instead of testing === or !==.
Words in this lesson (7)
- while loop
- A loop that tests its condition before each pass and runs its body while the condition is true. When the condition is false, the program continues after the loop.
- iteration
- One run of the block of a loop.
- loop condition
- The test that decides whether a loop runs another pass, such as floor < target. The loop ends the first time the test is false.
- infinite loop
- A loop whose condition never becomes false, so the loop never ends by itself.
- off-by-one error
- A mistake where a loop or a count goes one step too far or stops one step too early, such as a loop that never reaches the last element of an array.
- do-while loop
- A loop that runs its body first and tests its condition after each pass, so the body always runs at least once.
- guard
- A test on the left of && that makes the right side safe to compute, such as checking that a list is not empty before reading its first element.
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 / While
What does a while loop do?
A while loop repeats a block of code. It has a condition, written in parentheses after while, and a body: the block between the braces. Before each pass through the body, the program tests the condition. When the condition is true, the body runs once, and the program goes back to the test. When the condition is false, the loop ends, and the program continues with the line after the loop.
Each run of the body is called an iteration. Because the test comes first, a loop whose condition is false at the start runs its body zero times.
let floor = 1;
let target = 4;
while (floor < target) {
floor = floor + 1;
console.log("floor " + floor); // floor 2, 3, 4
}
console.log("doors open at " + floor); // after the loop A while loop tests its condition before each pass; when the condition is false, even at the start, the program continues after the loop.
Coming from Python: There is no while … else. The line after the loop runs the same way, whether the loop ran zero times or many.
Experiment
Follow a lift up to its floor
The condition is tested before every pass, with the value floor has at that moment. floor < target is true for 1, 2, and 3, so the body runs three times. After the third pass floor is 4, the test is false, and the loop ends. When floor starts at 4, the first test is already false, and the body never runs.
Why it matters To count the passes of a while loop, list the values its condition sees, and stop at the first one that makes the condition false.
Predict how many floor lines are printed. Then change let floor = 1; to let floor = 4; and predict again.
let floor = 1;
let target = 4;
while (floor < target) {
floor = floor + 1;
console.log("floor " + floor);
}
console.log("doors open at " + floor);
What to look for
At the while line, floor shows 1, 2, 3, and then 4. With 4, the test 4 < 4 is false, and the next stop is the last console.log line. The run prints floor 2, floor 3, floor 4, then doors open at 4. This debugger sometimes stops on the same line twice; that is normal. After the edit, the while line shows floor 4, the next stop is the last console.log line, and the run prints only doors open at 4: the body never ran.
Expected output
floor 2
floor 3
floor 4
doors open at 402 / Stopping
What must change for the loop to stop?
The condition is tested again before each pass, with the values the variables hold at that moment. The loop stops only when the condition becomes false. So the body must change something the condition reads, and change it toward false. In while (floor > 1), the condition reads floor, so every pass must lower floor.
When no pass changes what the condition reads, the condition stays true forever. This is an infinite loop: the program never reaches the line after it. Nothing reports an error; the program simply keeps running. In the stepper, a run that never ends stops after 240 recorded stops, with the message Stopped after 240 recorded stops.
A common way to get there is to change the wrong variable. Below, the body computes the next floor in below, but the condition reads floor. Without the line floor = below, below is 3 on every pass, and floor stays 4 forever.
let floor = 4;
while (floor > 1) {
const below = floor - 1;
console.log("down to " + below);
floor = below; // the change the condition needs
}
console.log("doors open at " + floor); Each pass must change something the loop condition reads, and move it toward false; otherwise the loop never stops.
Precisely: an infinite loop blocks the program
JavaScript runs one piece of code at a time in a page or a Node.js program. While a loop runs, no timer, click handler, or other code of that program can run. An infinite loop therefore freezes the whole page, not only the loop.
Experiment
Compute the next floor and forget to store it
The condition reads floor. The body computes a new value in below, but without floor = below, floor never changes, so floor > 1 is true on every test.
Why it matters When a loop never ends, find what its condition reads, then check that every pass, including a pass that ends early, changes it toward false.
Predict the three lines printed before doors open. Then delete the line floor = below; and predict what the loop does.
let floor = 4;
while (floor > 1) {
const below = floor - 1;
console.log("down to " + below);
floor = below;
}
console.log("doors open at " + floor);
What to look for
At the while line, floor shows 4, 3, 2, and then 1, and the run prints down to 3, down to 2, down to 1, then doors open at 1. At the start of a pass, below can still show its value from the pass before; the const line then gives below its new value. After the edit, floor shows 4 at every while line, and below shows 3: the body changes below, but the condition reads floor. The output fills with the same line, and the stepper ends with Stopped after 240 recorded stops. doors open is never printed.
Expected output
down to 3
down to 2
down to 1
doors open at 103 / After the loop
Where does the loop stop, and what is left in the variable?
When a while loop ends, its condition has just been tested and found false. So after the loop, the variable holds the first value that failed the test, not the last value the body used. Below, while (floor <= 6) counts floor up from 2, and after the loop floor is 7, a floor the lift never visited.
The stopping test also decides how many passes run. The lift should stop at floors 2, 3, 4, 5, and 6: five stops. With floor < 6, it stops four times, because the pass for floor 6 never runs. A loop that runs one pass too few or one too many has an off-by-one error. To check a stopping test, try two values: the last one that should run (6 must pass the test) and the first one that should not (7 must fail it).
Counting down works the same way, with the test turned around: a loop that lowers floor stops with > or >=. If you write < by mistake, as in while (floor < 1) with floor starting at 6, the test is false at once, and the body runs zero times.
let floor = 2;
let stops = 0;
while (floor <= 6) {
stops = stops + 1; // floors 2, 3, 4, 5, 6
floor = floor + 1;
}
console.log(stops, floor); // 5 7 After a while loop, the variable holds the first value that failed the condition. Check a stopping test with the last value that should run and the first that should not.
Experiment
Count the stops from floor 2 to floor 6
The test runs before each pass, with the value floor has then. 6 < 6 is false, so the body never runs for floor 6, and floor keeps the value that failed the test. With <=, 6 passes the test, the body runs for it, and the first value that fails is 7.
Why it matters When a count is one short or one too many, test the stopping condition with the last value that should run and the first value that should not.
The lift should stop at floors 2, 3, 4, 5, and 6. Predict both numbers printed. Then change floor < 6 to floor <= 6 and predict again.
let floor = 2;
let stops = 0;
while (floor < 6) {
stops = stops + 1;
floor = floor + 1;
}
console.log(stops + " stops, floor " + floor);
What to look for
At the while line, floor shows 2, 3, 4, 5, and 6, while stops shows 0, 1, 2, 3, and 4. 6 < 6 is false, so the pass for floor 6 never runs, and the run prints 4 stops, floor 6. After the edit, the body also runs for floor 6, the last test sees floor 7, and the run prints 5 stops, floor 7.
Expected output
4 stops, floor 604 / At least once
How do you run the body at least once?
A while loop tests before the first pass. Sometimes there is nothing to test until the body has run once. A do-while loop runs its body first and tests its condition after each pass: do { … } while (condition);. So its body always runs at least once, even when the condition is false from the start.
In the lift below, people board at each floor, 200 kg at a time, until the load reaches 500 kg. The loop goes on while load < 500, so it ends at the first load of 500 or more.
Choose do-while only when one pass is always right. When zero passes can be right, such as boarding when nobody is waiting, use while.
let floor = 1;
let load = 0;
do {
floor = floor + 1;
load = load + 200;
} while (load < 500); // tested after each pass
console.log(floor, load); // 4 600 A do-while loop runs its body before the first test, so the body always runs at least once.
Coming from Python: Python has no do-while. JavaScript has do { … } while (…);, and its body runs once before the first test.
Experiment
Board the lift before testing the load
A do-while loop runs its body first and tests after it. So the first test sees a load of 200, not 0. With a starting load of 900, the body runs once before anything is tested, and only then does load < 500 end the loop.
Why it matters Use a loop that tests after its body only when one pass is always right. When zero passes can be right, test before the body with while.
Predict the floor and the load printed. Then change let load = 0; to let load = 900;, a load already over 500, and predict again.
let floor = 1;
let load = 0;
do {
floor = floor + 1;
load = load + 200;
} while (load < 500);
console.log("full at floor " + floor + ", load " + load);
What to look for
The stepper never stops on the do line. It stops on the two body lines, then on the } while line, where load shows 200, 400, then 600. With 600, the test load < 500 is false, and the run prints full at floor 4, load 600. After the edit, the body still runs once before the first test: at the } while line, floor shows 2 and load 1100, and the next stop is the console.log line. The run prints full at floor 2, load 1100.
Expected output
full at floor 4, load 60005 / End of a list
How does a loop stop at the end of a list?
A loop that walks through a list must stop at the end of the list, even when it has not found what it looks for. An array is a list of values in order, written [0, 0, 3, 0]. waiting[i] is the value at position i, counting from 0, and waiting.length is the number of values. Arrays, lists, and vectors covers lists in detail.
The loop below looks for the first floor where someone is waiting: waiting[i] is the number of people waiting on floor i. Its condition has two tests joined with &&: i < waiting.length && waiting[i] === 0. The first test is a guard, as in Short-circuit evaluation. When i reaches the end of the list, the guard is false, and && never computes waiting[i], which does not exist there. With the two tests swapped, the last test reads waiting[4]. In JavaScript that gives undefined, so the loop happens to stop, but it has read a value that does not exist.
After the loop, check which test ended it. When i is still less than the length, the loop stopped at a floor where someone is waiting. Otherwise i equals the length: nobody is waiting, and there is no value at position i to read.
let waiting = [0, 0, 3, 0];
let i = 0;
while (i < waiting.length && waiting[i] === 0) {
i = i + 1;
}
if (i < waiting.length) {
console.log("go to floor " + i); // go to floor 2
} else {
console.log("nobody is waiting");
} Put the end-of-list test first in the loop condition, and after the loop, check which test ended it before reading at the position.
06 / Decimal steps
Why can a loop with decimal steps miss its end?
A loop that counts in decimal steps can miss its stopping value. As Floating-point numbers showed, adding 0.1 ten times gives 0.9999999999999999, not 1. The lift door below opens in steps of 0.1, from 0 (closed) to 1 (fully open).
With while (door < 1), the test after ten steps is 0.9999999999999999 < 1, which is true, so an eleventh step runs, and door ends at 1.0999999999999999. With while (door !== 1), it is worse: door goes past 1 without ever being equal to it, the condition never becomes false, and the loop never ends.
Count whole steps instead, and compute the decimal value from the count. tenths below goes 1, 2, 3, up to exactly 10, because whole numbers add without rounding. Then door is tenths / 10, which gives 1 at the end.
let tenths = 0;
while (tenths < 10) {
tenths = tenths + 1;
}
let door = tenths / 10;
console.log(door); // 1 Do not end a loop by testing a floating-point value with === or !==. Count whole steps, and compute the decimal value from the count.
Experiment
Open the door in steps of 0.1
Each + 0.1 is rounded to the nearest number JavaScript can store, and the small errors add up. After ten steps door is just below 1, so the < test passes once more. The !== test needs door to be exactly 1 at some test, and it never is.
Why it matters Do not end a loop by testing a floating-point value with === or !==. Count whole steps, and compute the decimal value from the count.
Predict how many steps run and the last value of door. Then change door < 1 to door !== 1 and predict again.
let door = 0;
let steps = 0;
while (door < 1) {
door = door + 0.1;
steps = steps + 1;
}
console.log(steps + " steps, door " + door);
What to look for
At the while line, door shows 0, 0.1, 0.2, 0.30000000000000004, 0.4, and so on. After ten steps it shows 0.9999999999999999, which is still less than 1, so the body runs again. The run prints 11 steps, door 1.0999999999999999. After the edit, door goes past 1 without ever being equal to it and keeps growing, and steps keeps counting. The stepper ends with Stopped after 240 recorded stops, and the output panel stays empty.
Expected output
11 steps, door 1.099999999999999907 / Try it
Can you explain a fresh example?
List the values the condition sees, one per pass, then the value that ends the loop. The next lesson, Function calls, shows how a program runs code that is written somewhere else.
let floor = 7;
let stops = 0;
while (floor > 2) {
floor = floor - 2;
stops = stops + 1;
}
console.log(stops + " stops, floor " + floor);
What is printed, and why is floor not 2 at the end?
Reveal the reasoning
3 stops, floor 1. The test sees 7, 5, and 3, which are all greater than 2, so the body runs three times. After the third pass floor is 1, and 1 > 2 is false. The loop ends at the first value that fails the test, so floor ends below 2: the lift went down in steps of 2 and passed floor 2 without stopping there.
Practice tickets
- LOOP-101 · The hot-drink machine leaves a full cup of water in the tank → Free
- LOOP-102 · Cooling a meeting room always takes the full hour → Sign in · Pro
- LOOP-103 · Letters go to the overflow box while slots are still empty → Sign in · Pro