SKU // 28

JavaScript Control Flow

Control flow is the order in which a program's statements actually run. By default, JavaScript reads top to bottom and left to right, one statement at a time. Everything in the last three sections (conditionals, loops, break and continue) exists to change that order. This lesson pulls it all together.

// The Building Blocks

Every program, no matter how large, is built from a handful of patterns:

Pattern What it does Tools
SequenceRun statements in orderjust write lines top to bottom
SelectionChoose which code runsif, else, switch, ? :
RepetitionRun code more than oncefor, while, do...while
JumpsLeave the normal path abruptlybreak, continue, return, throw
FunctionsRun a named block when it is calledfunction, return

// Default Flow: Sequence

let x = 5;          // 1st
let y = 6;          // 2nd
let z = x + y;      // 3rd: can use x and y, because they already exist
console.log(z);     // 4th
11

Order matters. Each statement can only use what the statements above it have already set up. Move the console.log to the top and it fails, because z does not exist yet.

// Selection: Conditional Flow

Conditionals make the flow branch: one path or another, depending on a condition. Only one branch runs; the other is skipped entirely.

let age = 15;
let text = "Unknown";

if (age >= 18) {
  text = "Adult";      // skipped
} else {
  text = "Minor";      // runs
}
console.log(text);
Minor

// Repetition: Loop Flow

Loops make the flow circle back: after the last line of the body, execution jumps back up to the condition, until the condition is false.

for (let i = 0; i < 3; i++) {
  console.log("The number is " + i);
}
The number is 0 The number is 1 The number is 2

// Jump Statements

Four keywords jump out of the normal flow:

breakExit the current loop or switchJS Break
continueSkip to the next loop passJS Continue
returnExit the current function, optionally sending back a valuebelow, and JS Functions
throwRaise an error, jumping to error handlingJS Errors
for (let i = 0; i < 10; i++) {
  if (i === 3) { break; }
  console.log("The number is " + i);
}
The number is 0 The number is 1 The number is 2

// Putting It Together

Here is a small program that uses sequence, selection, repetition, and jumps. It totals a day's sales, where a 0 means an empty order to skip, and a negative number means a data error that should halt processing:

const amounts = [25, 0, 40, -1, 60];
let revenue = 0;

for (const amount of amounts) {
  if (amount === 0) continue;        // skip empty orders
  if (amount < 0) {
    console.log("Stopping: bad amount");
    break;                             // halt processing
  }
  revenue += amount;
}
console.log("Revenue: $" + revenue);
Stopping: bad amount Revenue: $65

Trace it: 25 is added. 0 is skipped by continue. 40 is added (65 so far). -1 triggers break, so the 60 is never reached. Then the flow drops out of the loop to the final console.log.

// Function Flow

A function is a named, reusable block of code. It does not run where it is written; it runs when it is called, and then the flow returns to where the call was made. Functions get a whole section soon (JS Functions); here is just enough to see how they change the flow:

function multiply(p1, p2) {
  return p1 * p2;          // send the answer back to the caller
}

console.log("before");
let result = multiply(4, 3);  // flow jumps into the function, then back
console.log("after: " + result);
before after: 12

Reading top to bottom, the function's code appears first, but nothing happens there: it is only a definition. The flow goes "before", then jumps into multiply at the call, then comes back with 12.

return exits everything in a function

return ends the function immediately, even from inside a loop. That makes it a stronger jump than break, which only exits one loop:

function firstNegative(numbers) {
  for (const n of numbers) {
    if (n < 0) {
      return n;          // leaves the loop AND the function
    }
  }
  return null;           // only reached if nothing was found
}

console.log(firstNegative([4, 9, -2, 7, -5]));
console.log(firstNegative([1, 2, 3]));
-2 null

Guard clauses

Checking for the bad or special cases first and returning early keeps the main logic flat and readable. Compare two ways of pricing a ticket:

// Nested: the real work is buried three levels deep
function ticketPrice(age) {
  if (age >= 0) {
    if (age >= 5) {
      return 12;
    } else {
      return 0;
    }
  } else {
    return null;
  }
}

// Flat, with guard clauses: same results
function ticketPrice(age) {
  if (age < 0) return null;   // invalid age
  if (age < 5) return 0;      // under 5 is free
  return 12;                   // everyone else
}
Tip
Guard clauses read like a checklist: "If this is wrong, stop. If that is special, handle it and stop. Otherwise, do the real work." Most experienced developers prefer this style.

// JavaScript Is Single-Threaded

JavaScript runs on a single thread: it can only do one thing at a time. Every statement waits for the one before it to finish. That is why control flow is so predictable, and also why a slow loop is a problem:

let count = 0;
for (let i = 0; i < 3000000000; i++) {
  count++;           // billions of passes: takes several seconds
}

While that loop runs, the page is frozen: buttons do not respond and nothing redraws, because the single thread is busy. It is the same thing that happens with an infinite loop, just temporary.

For slow jobs like loading data from a server, JavaScript can run code asynchronously: start the job, carry on with other work, and come back when the result is ready. That is an advanced topic for much later, but it is why control flow in real web apps is not always strictly top to bottom.

Debugging Habit
When code does something unexpected, trace it by hand: write down each variable's value line by line, and at every if or loop, note which way it went. Most bugs are the program taking a path you did not expect.

// Quick Check

Which statement exits a function immediately, even from inside a loop?

  1. break
  2. continue
  3. return
  4. else
Show answer
C. return ends the whole function. break exits only the loop (the function carries on after it), and continue only skips to the next pass.

In the sales example, what would revenue be if the array were [10, 0, 20, 30]?

Show answer
60. The 0 is skipped, and with no negative number the loop never breaks, so 10 + 20 + 30 are all added.

A button on your page stops responding while a big calculation runs. What is the reason?

  1. The button's code has a SyntaxError
  2. JavaScript is single-threaded and is busy with the calculation
  3. Loops disable all buttons
  4. The browser is out of memory
Show answer
B. JavaScript can only do one thing at a time. Until the calculation finishes, it cannot respond to the click.

// Try It

  1. Trace the sales example on paper, then run it with its Try it Yourself button. Did you predict the output?
  2. Change the -1 to 15 and run it again. What is the new revenue?
  3. Click Open Playground and finish firstOver100: loop over the array and use return to hand back the first number over 100 (or null if there is none)
  4. In the Playground, add both ticketPrice versions (rename the second one ticketPrice2). Call each with -3, 2, and 30 and check they give the same answers
// Video Walkthroughs
Last updated: Oct 9, 2026, 11:59 PM ET