Skip to content
All free labsForma mission 01 · JavaScript

You searched for sneakers.
Why are hoodie results showing up?

Two searches. One screen. Find out how an older reply takes over, then test a repair that keeps the latest search in charge.

Start here if you can follow a small function and know that a promise represents a result that may arrive later. Learn explains the extra terms. No account needed.

The latest request owns the screen

Requests do not necessarily finish in the order they start. If every completed request writes to the screen, an older response can overwrite a newer search. This is a race: the visible result depends on which operation finishes when.

Keep a generation counter with the feature. Each request captures its own ticket before awaiting work. Compare that ticket with the current generation immediately before each visible state update.

Three kinds of update need the same check

  1. Results: an older success must not replace the current result.
  2. Errors: an older failure must not cover a newer success.
  3. Loading: older cleanup must not hide the current search’s waiting message.

A finally block runs when try finishes, including when it returns. Guarding only the successful result leaves other writes unprotected.

Follow the ticket in real code

This fixed example lets the newer result finish first and ignores the older result. It demonstrates the data guard; Your turn checks errors and loading too.

Follow the code

0 / 18 steps
javascript
let generation = 0;async function search(load) {  const ticket = ++generation;  const value = await load();  if (ticket !== generation) return;  console.log(value);}let releaseOld;const old = new Promise(resolve => { releaseOld = resolve; });search(() => old);search(() => Promise.resolve("new"));releaseOld("old");

Console output

0 lines

No output yet.

Ready to start

Press Play, or Step to go one line at a time.

More ways to follow this example

Call stack

No call running.

All captured values

No values captured at this pause.

Every step as text

This lists every pause, so it has more steps than the player.

  1. Step 1 · Before line 1

    Next, work out 0 and store its value under generation. Any calls on the right must finish first.

    search
    async function search(load) { … }
  2. Step 2 · Before line 8

    Next, run this instruction: let releaseOld;. Follow its effect on the highlighted names and output.

    generation
    0
  3. Step 3 · Before line 9

    Next, define the arrow function old. Defining it stores instructions for a later call; its body does not run yet.

    releaseOld
    undefined
  4. Step 4 · Before line 9

    Next, define the arrow function old. Defining it stores instructions for a later call; its body does not run yet.

    resolve
    function () { … }
  5. Step 5 · Before line 10

    Next, run this instruction: search(() => old);. Follow its effect on the highlighted names and output.

    releaseOld
    function () { … }
    old
    Promise (pending)
  6. Step 6 · Before line 3

    Next, work out ++generation and store its value under ticket. Any calls on the right must finish first.

    load
    () => old
  7. Step 7 · Before line 4

    Start or read the awaited operation. This function can pause its continuation here while the caller continues.

    ticket
    1
    generation
    1
  8. Step 8 · Before line 10

    Next, run this instruction: search(() => old);. Follow its effect on the highlighted names and output.

  9. Step 9 · Before line 11

    Next, run this instruction: search(() => Promise.resolve("new"));. Follow its effect on the highlighted names and output.

  10. Step 10 · Before line 3

    Next, work out ++generation and store its value under ticket. Any calls on the right must finish first.

    load
    () => Promise.resolve("new")
  11. Step 11 · Before line 4

    Start or read the awaited operation. This function can pause its continuation here while the caller continues.

    ticket
    2
    generation
    2
  12. Step 12 · Before line 11

    Next, run this instruction: search(() => Promise.resolve("new"));. Follow its effect on the highlighted names and output.

  13. Step 13 · Before line 12

    Next, run this instruction: releaseOld("old");. Follow its effect on the highlighted names and output.

  14. Step 14 · Before line 5

    Check the condition against the current values. A loop checks again before another repetition; an if chooses its matching branch.

    load
    () => Promise.resolve("new")
    ticket
    2
    value
    "new"
    old
    Promise (fulfilled)
  15. Step 15 · Before line 6

    Next, work out the values inside console.log, including any function calls, then print the results. A called function may run before the message appears.

  16. Step 16 · Output from line 6

    The console just gained this message: new. Printing a value does not by itself change the stored value.

    Console: new

  17. Step 17 · Return at line 7

    This call returns Promise (fulfilled) to its caller. Its caller can now use that result.

    return value
    Promise (fulfilled)
  18. Step 18 · Before line 5

    Check the condition against the current values. A loop checks again before another repetition; an if chooses its matching branch.

    load
    () => old
    ticket
    1
    value
    "old"
  19. Step 19 · Return at line 5

    This call returns Promise (fulfilled) to its caller. Its caller can now use that result.

    return value
    Promise (fulfilled)
  20. Step 20 · Finished

    The recording has ended. Compare the final stored values with what actually appeared in the output.

About this example

Recorded from this exact example running in JavaScript. Steps pause before a statement, at a return, or after console output. Built-in implementation details are omitted.

The player replays a recorded run of this exact code. It doesn’t run code you change.

What this model leaves out

The mission uses two controlled requests and predetermined results. It does not model a whole network. Cancellation may save work, but cannot replace the ownership checks. A disposed feature must invalidate its tickets too. Unrelated features need independent counters.