cf.completefrontendCode editorOpen lab
THE JAVASCRIPT FIELD GUIDE

Execution contexts & jobs in the spec

Read how ECMAScript tracks running and suspended code with execution contexts, environment records, realms, generators, and host-scheduled jobs.

By the end, you can
  • 01
    Read a contextIdentify the running context, its realm, originating script or module, and its environment components.
  • 02
    Track a pauseExplain why a generator or async function can suspend without losing its local state.
  • 03
    Locate the host boundaryDistinguish ECMAScript Jobs and enqueue hooks from a browser or Node event loop.

A context tracks one evaluation

When JavaScript calls a function, it needs to know which code is running and where to find its names. The specification describes that state with an execution context: a device for tracking one evaluation. It is not an object your code can inspect.

A function runs, then its caller printsPop out in the code editor (opens in a new tab)JavaScript
function greet(user) {
  return "Hi, " + user;
}
console.log(greet("Asha"));

Line 1 declares greet. Line 2 builds a greeting from its parameter user. Line 4 calls it with "Asha" and prints Hi, Asha. For the duration of the call, the function's evaluation has its own context.

The specification's definition

An execution context is a specification mechanism, not a JavaScript value or a required physical engine stack frame. Its job is to track evaluation, including where execution can continue after a pause. You cannot read the context through a property.

This lesson continues Environment Records. That lesson tells you where names live. Here we ask which context is using those records, when that context runs, and how later work starts. For a first look at ordinary calls, see the published call stack lesson.

The execution context stack

An execution context stack tracks contexts during evaluation. Its top entry is the running execution context. When control passes to code with a new context, the new context is pushed and becomes the running one. The caller waits.

One call and one returnPop out in the code editor (opens in a new tab)JavaScript
function getPrice() {
  return 3;
}
console.log(getPrice());

Line 1 declares getPrice. Line 2 returns 3. Line 4 calls it, then prints 3. While the call runs, the new function context is on top; after it returns, the caller can finish line 4.

Short excerpt: execution context stackText
"The running execution context is always the top element of this stack."
"The newly created execution context is pushed onto the stack and becomes the running execution context."

These quoted lines come from ECMA-262, Execution Contexts. They describe the specification's stack rule. The runnable example above shows the effect, but does not expose the contexts themselves.

A nested function callPop out in the code editor (opens in a new tab)JavaScript
function addTax(price) {
  return price + 2;
}
function total() {
  return addTax(3);
}
console.log(total());

In the larger example, line 7 calls total. Line 5 calls addTax with 3. Line 2 adds 2. The printed result is 5. The guided replay below uses the real getTotal and addTax functions; it is an instrumented replay, not an engine debugger.

Replay a nested call
Step 0 of 6Ready
Your turn: follow the blue line

Replay of instrumented example functions, not an engine debugger.

Running in
  1. script
Next: line 7
Click the blue line to take the next stepPop out in the code editor (opens in a new tab)JavaScript
function addTax(price) {  return price + 2;}function total() {  return addTax(3);}
CallStoreChangeResultRun = next line. Ran = already executed.
Recent returnsNothing yet. Start with the blue line.
A guided replay recorded from real JavaScript calls, not an engine debugger. Step follows executed statements; Back reviews a snapshot. Reset starts a fresh run.
Real-life analogyA notebook with the current page

You stop writing on one notebook page to use another. When you finish, you return to the earlier page where you left off.

In real life: One page is open
In JavaScript: One context is running
In real life: Open a new page
In JavaScript: Call into a new context
In real life: Keep the old page
In JavaScript: The caller can resume

Where the analogy stops: A context is specification state, not a literal notebook page or necessarily a physical stack frame.

The simple call pattern is last-in, first-out: addTax finishes before total. Later sections show why a suspended generator need not stay on the stack while it waits. For a visual treatment of calls, visit the call stack lesson.

What a context remembers

A context needs more than a position in a stack. Its code evaluation state records what is needed to run, pause, and resume evaluation. Other components connect the current code with its function, realm, and script or module of origin.

The function and its callerPop out in the code editor (opens in a new tab)JavaScript
function order(user) {
  return user + " ordered tea";
}
console.log(order("Asha"));

Line 1 defines order. Line 2 reads user and returns a string. Line 4 invokes the function and prints Asha ordered tea. In this call the Function component names order; in the surrounding script context it is null.

Short excerpt: common context componentsText
"code evaluation state" — state needed to perform, suspend, and resume evaluation
"Function" — the function object, or null for a Script or Module
"Realm" — the Realm Record used to access ECMAScript resources
"ScriptOrModule" — the originating Script Record or Module Record (or null)
Read the context component, not an imaginary JavaScript property
ComponentWhat the spec uses it forWhat it does not mean
code evaluation stateWhere evaluation can continue after a pause.It is specification state, not a readable JavaScript property.
FunctionThe active function object, or null for script/module code.Not a list of all functions waiting on the stack.
RealmThe realm whose intrinsic objects and global environment the code uses.Not the filename or module resolver.
ScriptOrModuleThe source Script Record or Module Record, or null when there is none.Not the live call stack or an import URL string.
LexicalEnvironmentThe Environment Record used to look up an identifier.Not necessarily the same record as VariableEnvironment.
VariableEnvironmentThe Environment Record for VariableStatement bindings.Not every lexical block's current bindings.
PrivateEnvironmentThe PrivateEnvironment Record for class private names, or null.Not a private field's value on an instance.
GeneratorThe generator associated with a generator execution context.Not present on every kind of context.

The excerpt and table follow the current Execution Contexts definition. Function is null for script or module evaluation. Realm identifies the ECMAScript resources available to associated code; ScriptOrModule identifies its originating record or can be null. None of these components is a promise to expose an engine data structure.

LexicalEnvironment vs VariableEnvironment

LexicalEnvironment identifies the Environment Record used to resolve names in this context. VariableEnvironment identifies the record that holds bindings from var statements. Both are records, not two JavaScript objects created by every line.

A block name beside a function varPop out in the code editor (opens in a new tab)JavaScript
function receipt() {
  var price = 3;
  if (true) {
    let note = "tea";
    console.log(note, price);
  }
}
receipt();

Line 2 sets price to 3 in the function. Line 3 enters a block. Line 4 makes the block-local note with value tea. Line 5 resolves both names and prints tea 3; line 8 calls the function. The current lexical record can change at a block while the function's var binding stays in its var scope.

Short excerpt: ECMAScript code context componentsText
"LexicalEnvironment" — resolves identifier references
"VariableEnvironment" — holds bindings made by VariableStatements
"PrivateEnvironment" — holds Private Names in the nearest containing class
"Generator" — the Generator this context evaluates, for generator contexts

The excerpt follows the spec's additional context state tables. PrivateEnvironment tracks private names declared by the nearest containing class; it is null when there is no containing class. The optional Generator component belongs to generator evaluations, not to every context.

An inner name hides, not overwritesPop out in the code editor (opens in a new tab)JavaScript
var price = 3;
{
  let price = 5;
  console.log(price);
}
console.log(price);

Line 1 sets the outer price to 3. Line 3 makes a different price in the block. Line 4 prints 5; line 6 prints 3. The block record points outward through [[OuterEnv]] for names it cannot find locally; it is not a new function call on the context stack.

Three related but different routes to a name
PartQuestion it answersDo not confuse it with
LexicalEnvironmentResolve a read of note inside the block.Can point to a new record when entering a block.
VariableEnvironmentKeep the var price binding in the function's var scope.Does not move into the inner block for that var.
[[OuterEnv]]Follow an outer record if the current record lacks a name.Connects scopes, not consecutive calls on the stack.

From ES3 variable objects to Environment Records

Older ECMAScript editions described a variable object as part of a context. That historical term is useful when reading old explanations, but it is not the current spec's general model. ECMA-262 now uses Environment Records with explicit operations and an outer link.

Two declarations, one ordinary readPop out in the code editor (opens in a new tab)JavaScript
var price = 3;
let note = "tea";
console.log(price, note);

Line 1 creates price with var. Line 2 creates note with let. Line 3 reads both and prints 3 tea. From JavaScript you read ordinary identifiers; you never get a variable-object API or an Environment Record to inspect.

The distinction matters when you encounter an old diagram claiming every scope is just one object. The current Environment Records definition says these are specification mechanisms that need not match an implementation artifact. Its declarative, object, global, function, and module forms explain cases a single simple object picture misses.

Use the current vocabulary

When reading today's spec, ask which Environment Record has the binding and what its [[OuterEnv]] points to. The earlier Environment Records lesson walks through those operations. This lesson focuses on which execution context holds the links while it runs.

This is a change of description, not a claim that an old browser physically stored every variable in one object or that modern engines must allocate a record for every block. The spec defines observable behavior; the implementation can optimize it.

Realms and ScriptOrModule

A realm provides intrinsic objects, a global object, and a global environment for code. The context's ScriptOrModule says which Script Record or Module Record its code came from. These are separate questions: which resources does the code use, and where did the code originate?

An array uses this realm's Array.prototypePop out in the code editor (opens in a new tab)JavaScript
const cart = [];
console.log(Object.getPrototypeOf(cart) === Array.prototype);

Line 1 creates an array. Line 2 compares its prototype to the current realm's Array.prototype and prints true. This snippet uses one realm; it does not create a second realm. The spec gives each realm its own intrinsic objects, so do not generalize this comparison across realms.

Script source is an origin, not a queuePop out in the code editor (opens in a new tab)JavaScript
function receipt() {
  return "tea";
}
console.log(receipt());

Line 1 declares receipt. Line 2 returns tea. Line 4 prints tea. ScriptOrModule can point at the originating script record even while the function's call context runs; it is not a string named receipt.js or a request to schedule another task.

The Realms definition describes intrinsics and a global environment. The context table names ScriptOrModule. For a fuller cross-realm example, see Realms and globals. A realm belongs to an agent, which owns a context stack and running context; an agent is itself a spec mechanism, not necessarily a physical thread.

Jobs and host hooks

A Job is a no-argument abstract closure that starts ECMAScript computation when no other computation is in progress in its agent. The host is the environment running the language, such as a browser or Node. Hosts schedule jobs; ECMA-262 sets constraints without specifying one universal event loop.

A Promise handler waits for the scriptPop out in the code editor (opens in a new tab)JavaScript
console.log("start");
Promise.resolve("tea").then((order) => console.log(order));
console.log("end");

Line 1 prints start. Line 2 registers a reaction for the fulfilled promise, but does not call it inline. Line 3 prints end. After that, the reaction prints tea. The output order is start, end, tea.

Short excerpt: Jobs and their orderingText
"A Job is an Abstract Closure with no parameters that initiates an ECMAScript computation when no other ECMAScript computation is currently in progress."
"Jobs must run in the same order as the HostEnqueuePromiseJob invocations that scheduled them."

The short quotes are from Jobs and Host Operations. HostEnqueuePromiseJob schedules Promise-related jobs and must preserve the order of its enqueue calls. HostEnqueueGenericJob schedules a generic job without those added priority and order constraints.

Promise reaction orderPop out in the code editor (opens in a new tab)JavaScript
Promise.resolve().then(() => console.log("first"));
Promise.resolve().then(() => console.log("second"));
console.log("now");

Lines 1 and 2 queue reactions in that order. Line 3 prints now first; then the reactions print first and second. The test runs the real Promise code asynchronously; a script cannot inspect the host's job queue directly.

Do not use stack, job, and host as synonyms
TermRoleOne useful distinction
Execution contextState while code runs or is suspended.Tracks one evaluation, not a work queue.
Execution context stackThe contexts currently stacked for an agent.Top is the running context.
JobA no-argument abstract closure that starts computation later.A host schedules it when no other computation runs.
HostEnqueuePromiseJobHost hook for Promise-related jobs with ordering constraints.Promise jobs scheduled through it run in enqueue order.
HostEnqueueGenericJobHost hook for a job with no extra priority or order constraint.Does not specify a browser task queue.
A timer is host schedulingPop out in the code editor (opens in a new tab)JavaScript
console.log("script");
setTimeout(() => console.log("timer"), 0);
Promise.resolve().then(() => console.log("promise"));

Line 1 logs script. Line 2 requests a host timer. Line 3 registers a Promise reaction. In a browser the timer is not a Promise job; the browser's event loop controls when its callback runs. ECMA-262's job definition alone does not promise a universal timer order across every host.

To study that scheduling in detail, follow the published event loop and microtasks lessons. The spec here tells us why a reaction is later work and why jobs in one agent do not overlap; HTML and Node provide their host rules.

Generator execution contexts can pause

A generator returns an iterator whose next() method resumes its body. At yield, its evaluation is suspended and the caller runs again. The saved state is available for a later next(). That is different from leaving an ordinary nested call running forever.

Yield, then returnPop out in the code editor (opens in a new tab)JavaScript
function* orders() {
  yield "tea";
  return "done";
}
const order = orders();
console.log(order.next().value);
console.log(order.next().value);

Line 1 defines the generator. Line 2 yields tea. Line 5 creates its iterator without running the body. Line 6 resumes it and prints tea. Line 7 resumes after the yield, returns, and prints done. The context can leave the running stack at yield while its state remains saved.

Short excerpt: generator suspend and resumeText
"Set gen.[[GeneratorState]] to SUSPENDED-YIELD."
"Return ? RunCallerContext(iteratorResult)."
"Return ? RunSuspendedContext(genContext, NormalCompletion(value))."

These short steps paraphrase notation only where shown; see GeneratorYield, GeneratorResume, and RunSuspendedContext for the full algorithms. The context's Generator component connects it with the generator object; its evaluation state says where to continue.

Playground: advance one generator
Generator whose next call changes countPop out in the code editor (opens in a new tab)JavaScript
function* countOrders() {  let count = 1;  yield count;  count += 1;  yield count;}const orders = countOrders();console.log(orders.next().value);console.log(orders.next().value);
Teaching modelnot started
countnot started

Body has not run.

printednone

The third call ends the generator without another log.

Try it yourself

The generator object exists, but its body has not run. Press Next call.

This is a teaching model of the displayed generator, not a view into engine state. Reset creates the initial state again.
Replay two generator next calls
Step 0 of 7Ready
Your turn: follow the blue line

Replay of instrumented generator next calls, not an engine debugger.

Running in
  1. script
Next: line 7
Click the blue line to take the next stepPop out in the code editor (opens in a new tab)JavaScript
function* countOrders() {  let count = 1;  yield count;  count += 1;  yield count;}console.log(orders.next().value);console.log(orders.next().value);
CallStoreChangeResultRun = next line. Ran = already executed.
Recent returnsNothing yet. Start with the blue line.
A guided replay recorded from real JavaScript calls, not an engine debugger. Step follows executed statements; Back reviews a snapshot. Reset starts a fresh run.

Press Next call once to see 1, again to see 2, then again to finish. The displayed state is a teaching model, not an engine inspection. Its source is runnable and a test runs all three calls. Reset restores the state before the first next() call.

Await also suspends evaluationPop out in the code editor (opens in a new tab)JavaScript
async function receipt() {
  console.log("before");
  await Promise.resolve();
  console.log("after");
}
receipt();
console.log("outside");

Line 2 logs before; line 3 awaits even a fulfilled promise. Line 6 calls receipt and line 7 logs outside. After the Promise reaction resumes the async evaluation, line 4 logs after. The output is before, outside, after. This is a resumption, not a second call to the function body from its first line.

Await captures the async context and arranges its resumption through Promise reactions. For the full language-level walkthrough, visit the published async/await lesson. The same stack model cannot explain a context held while another computation runs unless it includes suspension.

Use the model in an app

Suppose a checkout page calculates a total, updates a message after a Promise, and then receives a click. These are not all one uninterrupted call. A synchronous function call uses the current stack; a Promise reaction starts later as a job; the browser supplies the click and timer scheduling rules.

A checkout calculationPop out in the code editor (opens in a new tab)JavaScript
function showTotal(price) {
  return price + 2;
}
console.log(showTotal(3));

Line 1 declares showTotal. Line 2 adds 2 to the price. Line 4 calls it with 3 and prints 5. That is an ordinary nested call: the function's context runs on top, then the script continues.

One agent's ordinary valuePop out in the code editor (opens in a new tab)JavaScript
const order = { item: "tea" };
console.log(order.item);

Line 1 stores an order object. Line 2 prints tea. The code does not create an agent. An agent is the spec's owner of a context stack and running context, among other state. The Agents definition allows separate agents to make progress independently; it does not say each is a dedicated hardware thread.

For a real site, use this model to identify which name belongs to which scope, where a function pauses, and which transition belongs to the host. When debugging order, log at boundaries rather than assuming every callback remains on the original synchronous stack. Microtasks covers the browser behavior; Node's event loop covers its different host.

Context, host, or realm?
  • Where the running code resolves note
  • The function currently being evaluated
  • The originating Script Record or Module Record
  • Where a Promise reaction job is enqueued
  • Scheduling an unconstrained generic job
  • Which Array.prototype belongs to the code
Try it yourself
0 of 6 correct

Place each spec clue where it belongs, then compare the explanation.

Choose a category for every card. You can change an answer at any time; Reset clears them all.

Common mix-ups

A stack, a job, a realm, and an environment answer different questions. Keeping them separate makes spec algorithms less mysterious. Start with one small call before deciding which part an unfamiliar step describes.

One call does not create a jobPop out in the code editor (opens in a new tab)JavaScript
function getPrice() {
  return 3;
}
console.log(getPrice());

Line 1 defines getPrice. Line 2 returns 3. Line 4 prints 3 after a synchronous call. No Promise handler or queued job is required for that ordinary call.

  • "Every context is one physical stack frame." A context is a spec device and can suspend.
  • "A generator stays running while paused." Its context can leave the stack and later resume.
  • "A Realm is a job queue." It provides intrinsics and a global environment.
  • "The spec defines the browser's event loop." It defines jobs and hooks; the host supplies scheduling.
  • "VariableEnvironment always means one object with every variable." Both environment components name records.
Similar names with different jobs
NameWhat it isNot the same as
A contextSpecification state for evaluating code; it may be suspended.A visible JavaScript object or exactly one native stack frame.
A realmA set of intrinsics and a global environment for code.The script file, the job queue, or necessarily an agent.
A Promise jobSpecified work sent to a host hook with ordering rules.A timer callback or a universal event-loop algorithm.
A suspended generatorRetains state until resumed or completed.An actively running call on the stack the whole time.

If two descriptions seem contradictory, check whether one talks about an active synchronous call and the other about a suspended evaluation. The usual nested-call stack behavior is correct, but it is not the entire story once generators and await are involved.

Practice exercises

Start by predicting an ordinary call. Then track two bindings, a Promise reaction, and a suspended context. Use the answer checker for the short response and compare the worked solution with the code you ran.

Exercise 1 · Warm-upPredict the nested call

Read the last line first: it calls total. Trace the inner call before you write the printed number. Which context is running while the addition happens?

Starter codePop out in the code editor (opens in a new tab)JavaScript
function addTax(price) {
  return price + 2;
}
function total() {
  return addTax(3);
}
console.log(total());

Answer, then press Check. Spacing and letter case don’t matter.

    Exercise 2 · Warm-upFollow a block binding

    The block declares a new price. Write the two printed numbers in order. Which environment is consulted first for the read inside the block?

    Starter codePop out in the code editor (opens in a new tab)JavaScript
    var price = 3;
    {
      let price = 5;
      console.log(price);
    }
    console.log(price);

    Answer, then press Check. Spacing and letter case don’t matter.

      Exercise 3 · PracticePredict a Promise reaction

      A customer orders tea, and a Promise handler will print the item. Write the three console lines in order. The Promise is already fulfilled, but the handler still waits.

      Starter codePop out in the code editor (opens in a new tab)JavaScript
      console.log("start");
      Promise.resolve("tea").then((order) => console.log(order));
      console.log("end");

      Answer, then press Check. Spacing and letter case don’t matter.

        Exercise 4 · PracticeFind the resume point

        The first next() yields tea and pauses the generator. On the second next(), where does evaluation continue? Describe the place in a few words.

        Starter codePop out in the code editor (opens in a new tab)JavaScript
        function* orders() {
          yield "tea";
          return "done";
        }
        const order = orders();
        console.log(order.next().value);
        console.log(order.next().value);

        Answer, then press Check. Spacing and letter case don’t matter.

          Exercise 5 · ChallengeFind the running context in checkout

          Your checkout page shows a price plus a small fee. While the addition runs, what function owns the running context? Name it before changing any app code.

          Starter codePop out in the code editor (opens in a new tab)JavaScript
          function showTotal(price) {
            return price + 2;
          }
          console.log(showTotal(3));

          Answer, then press Check. Spacing and letter case don’t matter.

            Exercise 6 · ChallengeFollow await on a real page

            Imagine this as a receipt update after checkout. Predict the three logs, then identify which work occurs after the function pauses at await.

            Starter codePop out in the code editor (opens in a new tab)JavaScript
            async function receipt() {
              console.log("before");
              await Promise.resolve();
              console.log("after");
            }
            receipt();
            console.log("outside");

            Answer, then press Check. Spacing and letter case don’t matter.

              Check your understanding

              Trace a call before thinking about scheduling. For each question, decide whether you need the running context, an environment record, a realm, or a later job.

              Execution contexts and jobs quiz · 7 questionsScore: first tries count
              1. Question 1 of 7When total calls addTax, which execution context runs?

                Choose an answer to see the explanation.

              2. Question 2 of 7What does this nested call print?

                Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
                function addTax(price) { return price + 2; }
                console.log(addTax(3));

                Choose an answer to see the explanation.

              3. Question 3 of 7Which component resolves the name note inside a block?

                Choose an answer to see the explanation.

              4. Question 4 of 7What does this shadowed binding print?

                Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
                var price = 3;
                { let price = 5; console.log(price); }
                console.log(price);

                Choose an answer to see the explanation.

              5. Question 5 of 7Who chooses the browser or Node event loop details?

                Choose an answer to see the explanation.

              6. Question 6 of 7What does this Promise program print?

                Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
                console.log("start");
                Promise.resolve("tea").then((item) => console.log(item));
                console.log("end");

                Choose an answer to see the explanation.

              7. Question 7 of 7What happens when a generator reaches yield?

                Choose an answer to see the explanation.

              When a code question surprises you, run the displayed snippet and then explain each output with the relevant context transition. The quiz is about spec vocabulary, not engine timings or a guessed implementation detail.

              Key takeaways

              The specification keeps three questions apart: what is running now, what state is saved for later, and who schedules later work. Ordinary calls and resumable code use the same vocabulary without behaving identically.

              • The top of an agent's execution context stack is its running context.
              • Code evaluation state can be saved and resumed. A context is a spec device, not an exposed object.
              • LexicalEnvironment resolves names; VariableEnvironment tracks var bindings; PrivateEnvironment tracks class private names.
              • Function, Realm, ScriptOrModule, and optional Generator answer different questions about an evaluation.
              • Promise jobs use HostEnqueuePromiseJob; the host supplies the event loop while obeying spec constraints.
              • Generators yield and resume. Await also suspends evaluation before a Promise reaction resumes it.

              Remember the one-liner.
              A context tracks evaluation; a job starts later computation; the host schedules it.

              Coming next: Function calls: [[Call]] & [[Construct]]. You will see how a function invocation creates and prepares the context we have been tracking. Revisit Environment Records for the binding side of this story.

              CompleteFrontend Clear concepts. Working examples.