Promise in JavaScript: handling a result that arrives later

A Promise stands for a value that does not exist yet: its three states, then and await, and the four ways to wait on several at once.
3 min read
Believemy logo

A network call takes two hundred milliseconds to answer. JavaScript itself cannot wait: it runs its statements through without ever pausing. So it needed a way to declare what should happen once the answer arrives, without freezing the page.

A promise is that in-between object. It stands for a result that does not exist yet, and may never exist at all.


Definition

A Promise is an object standing for the outcome of an asynchronous operation: a value delivered later, or a failure. You never take that value directly, you register in advance the work to run on it.

JAVASCRIPT
const readInvoice = async (id) => {
  if (id !== 42) throw new Error("Invoice not found");
  return { id, amount: 19 };
};

readInvoice(42)
  .then((f) => console.log(f.amount))   // 19
  .catch((err) => console.log(err.message))
  .finally(() => console.log("done"));

then receives the value on success, catch receives the Error on failure, finally runs in both cases. All three return a new promise, which is what makes chaining possible.


The three states

StateWhat it meansWhat causes it
PendingThe result is not knownThe starting state
FulfilledA value is availableA resolve or a return
RejectedAn error is availableA reject or a throw

The move is final: a fulfilled promise never goes back to pending and its result never changes again.

Good to know

A promise starts when it is created, not when it is awaited. Calling an asynchronous function kicks off the work right away, even if the await shows up ten lines later.


await, the same thing more readably

Inside an async function, await pauses what follows until the promise settles, then hands over its value. Failures become catchable again by an ordinary try.

JAVASCRIPT
async function showInvoice(id) {
  try {
    const invoice = await readInvoice(id);
    console.log(invoice.amount);
  } catch (err) {
    console.log("Unavailable:", err.message);
  }
}

The code reads top to bottom although it does not run in that order, and the gain becomes considerable across five chained calls.


Waiting on several promises

MethodWhat it hands back
Promise.allEvery value, or the first failure
Promise.allSettledThe outcome of each one, never failing
Promise.raceThe first to settle, success or failure
Promise.anyThe first to fulfill, failures ignored
JAVASCRIPT
async function loadEverything() {
  const [profile, invoices] = await Promise.all([
    readProfile(),
    readInvoices(),
  ]);
  console.log(profile, invoices);   // both calls ran together
}


Frequently asked questions

Question

Is new Promise still worth writing?

Rarely. Any function declared async already returns a promise, and modern interfaces produce one on their own. The constructor is now only there to wrap an older callback-based interface, such as setTimeout.

Question

What happens when nobody catches a rejection?

The failure travels up to the engine as an unhandled rejection: in a browser it ends up in the console, under Node.js it stops the process as of version 15. A promise with no catch is therefore a real risk, not a warning.

Question

Chain the await calls or reach for Promise.all?

It depends on how the calls relate. If the second one needs the first one's result, the sequence is unavoidable. If they are independent, starting them together divides the wait by their count, a reflex drilled in the JavaScript course.

Related terms

Discover our javaScript glossary

Every word of JavaScript explained simply: keywords, built-in objects, methods, errors and concepts. Clear definitions and examples that actually run, to learn and to troubleshoot.

Share this article

Want to help us? Share this article on your networks or even better: on your site, in an article or in your newsletter.