What A Promise Actually Is
The last post ended in callback hell, deeply nested and hard to read. Promises are the actual way out, and seeing one appear is the first step.
The last post ended in callback hell, a staircase of nested requests that only gets worse the more steps get chained. Promises are the actual way out. Before learning to use one, it helps to just see one appear.
Replacing XMLHttpRequest with fetch
The modern way to make an AJAX call is the fetch function, and it replaces the entire XMLHttpRequest setup from the last two posts with one line.
Hit Run. No .open(), no .send(), just a URL in, and something comes back immediately: Promise {<pending>}. The country data hasn't arrived yet, so whatever this is, it isn't that.
A Promise Is a Placeholder for a Future Value
That something is a Promise: an object that stands in for a value that doesn't exist yet.
Think of dropping a shirt off at a dry cleaner. The counter hands you a claim ticket immediately, not your clean shirt, just a placeholder for it, so you can walk off and do something else instead of standing at the counter until the cleaning is done. A Promise does the same job for an API response: it exists the moment the request starts, not once the response arrives.
The Promise Lifecycle: Pending, Then Settled Once
A Promise starts pending, the ticket stage, nothing to collect yet.
It eventually settles, one of two ways. Fulfilled means the task succeeded and the value is ready, the shirt's on the rack. Rejected means something went wrong instead, the shirt got lost, and there's a reason available for why.
A very ordinary rejection reason for fetch specifically: the user's device is offline, with no way to reach the server at all.
ExpandThe lifecycle of a Promise: pending while the task runs, then settling exactly once into either fulfilled or rejected, a state that never changes afterward.
Once it settles, that's permanent. A redeemed ticket doesn't go back to being unredeemed.
Why Promises Are Worth the Switch
Two real advantages come with this switch, not just nicer syntax.
First, consuming the eventual result doesn't require registering a separate event listener the way XMLHttpRequest did, that machinery is built into the Promise itself.
Second, and this is the actual escape route: Promises chain instead of nest. Handing a claim ticket to a friend, who picks up the shirt and drops off a second load in the same trip, is a completely different shape from standing at the counter for each step yourself. That chaining is what replaces the pyramid from callback hell, and it's the subject of the next post.
Building a Promise vs. Consuming One
fetch already handed back a built Promise, ready to be consumed. Most code only ever does that half, reacting to a Promise something else created.
Building one from scratch, the way fetch does internally, is a separate skill for a separate post. Promises themselves shipped in ES6 back in 2015, so this isn't new machinery, just the point in the course where it's finally worth reaching for.
The Essentials
- A Promise is a placeholder object for a future value, not the value itself.
fetch()returns a Promise immediately, pending until the network request actually finishes.- A Promise settles exactly once, into fulfilled or rejected, and never changes after that.
- Promises replace nested callbacks with chaining, which is the actual escape from callback hell.
- Most code only consumes Promises; something else, like
fetch, builds and returns them.
Keep reading