Skip to content

Lab · JavaScript

What runs now? What waits?

A program’s lines and its messages can appear in different orders. Start with four instructions and learn why, one step at a time.

Explore all labs
Preview · JavaScript

Why do the messages appear in this order?

Choose another example

Choosing a different example starts a new run. Your explanation preference stays the same.

Experiment

Start with a question

Why can a later instruction produce a message first?

Web pages often need to wait for something without mixing up the work they are doing. This lesson explains one rule for deciding what runs next.

Before you start: You do not need to write code. We’ll explain the notation in this example. This is an introduction to execution order, not a full JavaScript course.

Your goal: Predict the order of immediate messages, a ready promise callback and a timer callback, and explain your prediction.

  1. Begin with an ordinary instruction

    JavaScript is a language used to give a web page instructions. console.log("A") displays A in a message log called the console. Two ordinary log instructions run in the order the program reaches them.

  2. Some instructions arrange work for later

    A function is a group of instructions. A callback is a function given to another operation to call later. setTimeout(callback, 0) asks for a timer callback after a minimum delay of zero milliseconds. It does not interrupt the code already running.

  3. Ready does not mean running

    A promise represents a result that may become available. .then(callback) arranges work to do when that result is ready. Promise.resolve() supplies an already available result, but its .then callback still waits. In these examples, ready promise callbacks wait in the microtask queue, which is checked before timer callbacks in the task queue.

Words you’ll see

Call stack
The record of active function calls. Think of the bench’s raised platform as the place where code is running now.
Synchronous code and a job
Synchronous code runs immediately, in order. A job is one piece of JavaScript work, such as the initial script or a callback. It finishes before another job starts.
Queue
An ordered waiting list. Waiting work cannot run while the current JavaScript job is still executing.
Microtask and task
Two categories of waiting work. Here, promise callbacks are microtasks and timer callbacks are tasks. These names describe scheduling rules, not how fast the code is.

Read the symbols without writing code

() => console.log("B") is a callback: a small function that will display B when it is called. setTimeout(..., 0) hands a callback to a timer. .then(...) hands one to a promise. The dots in this explanation stand for the callback’s code.

In the example below, look for ordinary console.log lines first. Then find the work inside a timer or a promise callback. Those lines are arranged now and run later.

How JavaScript decides what runs next

First, finish the JavaScript job that is already running. Scheduling a callback does not run it immediately. The browser can keep track of a timer while the script continues, but the timer callback waits for its turn.

Next, run the ready microtasks. In these examples, those are the promise callbacks. Keep checking that queue until it is empty, including callbacks added by other microtasks.

Then run one waiting task, such as a timer callback. When it finishes, check the microtasks again before taking another task. This scheduling cycle is the event loop. A 0 ms timer requests no minimum waiting time; it does not jump ahead of the current job or ready microtasks.

Read this example together: Promise vs. timer

Synchronous code finishes first. Then the event loop empties the microtask queue (promises) before it takes the next task (timers), even a 0 ms one.

Read from top to bottom
  1. console.log("A");
  2. setTimeout(() => console.log("B"), 0);
  3. Promise.resolve().then(() => console.log("C"));
  4. console.log("D");

Worked example

  1. Line 1 displays A immediately. Line 2 arranges B for a timer callback; it does not display B yet.
  2. Line 3 arranges C for a ready promise callback. Line 4 displays D immediately. The messages so far are A D.
  3. The script has finished. The ready promise callback runs next and displays C.
  4. The microtask waiting list is empty. The timer callback can now run and display B.
  5. Result: A D C B.

Now use Step on the bench below. Each token stands for a piece of code; the tracks are waiting lists, and the raised platform shows the running code. Read the changing explanation beside it. The tray collects the messages in the order they appear.

Where the shop picture breaks: a real cashier might pause a customer to help someone else, but JavaScript never does. A job always runs to the end before anything else starts. This models the fixed examples here, not every feature of a browser or JavaScript.

Reference: JavaScript execution model (MDN)

mainBCADCB

Step 1 of 14

Reduced motion is on. Use Step to inspect one still position at a time.

What’s happening

Step 1 of 14

The script starts

The whole script is one job on the call stack. It runs top to bottom without stopping.

promise-vs-timer.js
  1. console.log("A");
  2. setTimeout(() => console.log("B"), 0);
  3. Promise.resolve().then(() => console.log("C"));
  4. console.log("D");

A free lesson with fixed examples. It introduces this scheduling rule, rather than executing arbitrary code or teaching the whole JavaScript language.

Keep exploring: step through call, apply, and bind, or fix an outdated search result.