What Is a Promise?
Most of the JavaScript you have written so far runs synchronously — line by line, in order, each line completing before the next one starts. That model breaks down the moment you need to wait for something: a network request, a timer, reading a file. Waiting synchronously would freeze the browser.
Promises are JavaScript’s solution to asynchronous operations — a way to say “start this work, and call me back when it’s done.”
The problem with waiting
Section titled “The problem with waiting”Imagine fetching exchange rates from an API:
// Imaginary synchronous fetch — this is NOT how it worksconst rates = fetchRates(); // this would freeze the browser for 1-2 secondsconsole.log(rates);If JavaScript waited here, nothing on the page could respond to user interaction — no scrolling, no button clicks, no animations. The browser would appear frozen.
Instead, JavaScript starts the network request and immediately moves on. When the response arrives (milliseconds or seconds later), a callback runs with the result. This is the event loop model.
What a Promise is
Section titled “What a Promise is”A Promise is an object that represents the eventual result of an asynchronous operation. At any point in time, a Promise is in one of three states:
- Pending — the operation has started but not yet completed
- Fulfilled — the operation succeeded; the Promise has a result value
- Rejected — the operation failed; the Promise has an error reason
const promise = fetch('https://open.er-api.com/v6/latest/USD');// promise is immediately in the 'pending' state// The network request is happening in the backgroundA Promise can only move from pending to either fulfilled or rejected — never backward, and only once. Once settled (fulfilled or rejected), it stays that way.
Creating a Promise manually
Section titled “Creating a Promise manually”You can create a Promise with new Promise(executor). The executor is a function that receives two callbacks: resolve (call this on success) and reject (call this on failure):
function wait(ms) { return new Promise(resolve => { setTimeout(resolve, ms); });}
wait(1000).then(() => console.log('1 second passed'));In practice you rarely create Promises manually — you consume Promises returned by APIs like fetch. But seeing how they are constructed helps explain what “pending,” “fulfilled,” and “rejected” mean.
The Promise is not the value — it is a placeholder
Section titled “The Promise is not the value — it is a placeholder”This distinction trips up every developer learning Promises:
const promise = fetch('https://open.er-api.com/v6/latest/USD');console.log(promise); // Promise { <pending> } — NOT the response datapromise is not the exchange rate data. It is a Promise object. To get the data, you must tell the Promise what to do when it resolves — that is what .then() is for (next lesson).
Why this design
Section titled “Why this design”The Promise model lets JavaScript stay non-blocking:
- You call an async function — it starts the operation and immediately returns a Promise
- Your code continues running (handling other events, updating the UI)
- When the operation completes, the Promise settles and your
.thencallback runs
The browser never freezes. The event loop keeps spinning. Your code stays responsive.
Exercise
Section titled “Exercise”Open the browser console on any page and run:
const p = fetch('https://open.er-api.com/v6/latest/USD');console.log(p);console.log(typeof p);console.log(p instanceof Promise);Observe that:
plogs asPromise { <pending> }(or similar)typeof pis'object'p instanceof Promiseistrue
This confirms that fetch returns a Promise, not the response data.
- JavaScript is single-threaded — waiting synchronously would freeze the page.
- A Promise represents the eventual result of an async operation: pending → fulfilled or rejected.
- You do not get the value from a Promise directly — you schedule work to run when it resolves.
fetchreturns a Promise immediately. The actual network response arrives later.