Promises The Two Pronged Approach

Promises changed the game in ES6 by giving us a direct placeholder in JavaScript for work happening in the background.

April 25, 20263 min read3 / 5

Until ES6, interacting with the browser's background features was a one-way street. We passed a function to a facade like setTimeout, and it was "called back" into JavaScript much later. This was the Callback style.

But in ES6, we got a much more powerful tool: Promises. These are what I call Two-Pronged Facade Functions.

The Essentials

  1. The Two Prongs: fetch triggers work in the browser's Network API and returns a special object in JavaScript immediately.
  2. The Promise Object: A placeholder with two key properties: value (the data) and onFulfilled (the functions to run).
  3. .then(): A method used to add functions to the onFulfilled array.
  4. The Hidden Bond: The browser network request is intimately linked to the JS Promise object; it updates the value property automatically upon completion.

The Network Portal: fetch

When we use fetch, we are speaking to the internet.

JavaScript
function display(data) { console.log(data); } const futureData = fetch('https://tiktok.com/will'); futureData.then(display); console.log("Me first!");

Prong 1: The Browser (Background)

The moment we call fetch, the browser's Network API kicks off. It sends an HTTP request (a "GET" request by default) to TikTok's servers. This work happens entirely outside of the JavaScript engine.

Prong 2: JavaScript (Foreground)

Simultaneously, fetch returns a brand new object into JavaScript memory: a Promise object.

Initially, this object looks like this:

  • value: undefined
  • onFulfilled: [] (an empty array)

By assigning the result of fetch to futureData, we now have a live label in our memory that tracks our background work.

Attaching Behavior: .then()

We don't want to just get data; we want to do something with it. We use .then(display) to push our display function into that hidden onFulfilled array.

Think of .then as "Add this function to run later." It doesn't run the function now; it just stores it for when the data arrives.

The Completion Trace

  1. At 270ms: The network request returns with our data: "cute puppy".
  2. Automatic Update: Because of the hidden bond, the browser goes back into JavaScript memory and updates futureData.value to "cute puppy".
  3. Automatic Trigger: JavaScript sees the value is no longer undefined. It automatically triggers any functions in the onFulfilled array, passing the value in as the argument.
JavaScript Execution Engine
Thread of Execution
1function display(data) { console.log(data); }
2const futureData = fetch('tiktok.com/will');
3futureData.then(display);
4console.log("Me first!");

Step 1:fetch immediately returns a Promise object. The network request is sent in the background.

Memory
displayf
futureData{ value: undef, onFulfilled: [] }
Call Stack
Global
Bottom of Stack

Why This is Better

Unlike the old style, we now have a reference to our background work (futureData) right in our JavaScript code. We can pass this object around, add multiple functions to it, and handle its state in a much more readable, pseudo-synchronous way.

But there's a catch. If both a setTimeout and a fetch finish at the same time, which one gets to run first? In the next part, we'll discover the Microtask Queue and the rules of priority.

Further Reading and Watching