Learn by reading through in order

Promise — Receiving Async Results with then and catch

Receive the result of a slow operation later with Promise: its three states, then / catch / finally, chaining, and Promise.all.

Some operations, like submitting an order or checking stock, take a while to return a result. To keep the page from freezing in the meantime, you write this kind of code so that it moves on to the next line right away and picks up the result later. This style is called asynchronous programming.

This article covers Promise, which represents a result that's settled later, and Promise.all, which waits for several Promises at once.

Receiving a Result Later — Promise and then

Say it takes 100ms to hear back about seat availability. setTimeout (a function that calls a function once, after the given number of milliseconds) returns immediately, and execution moves on to the next line, so you can't return an answer that arrives 100ms later from your function.

Instead, you return a Promise (an object representing a result that's settled later), and the answer is delivered through it. The function you pass to new Promise((resolve) => { ... }) is called immediately, and calling resolve(value) inside it moves the Promise from pending (waiting for a result) to fulfilled (succeeded). The function you pass to the then method is called with that value once the Promise is fulfilled.

// Returns a Promise that's fulfilled with value after ms milliseconds
function delay(ms, value) {
  return new Promise((resolve) => {
    setTimeout(() => resolve(value), ms);
  });
}

// Start the check (still pending at this point)
const seats = delay(100, "12 seats left");

// Pass the function to call once it's fulfilled
seats.then((message) => {
  console.log(message);                    // 12 seats left (printed 2nd)
});

console.log("Checking seat availability");  // Checking seat availability (printed 1st)
The Function Passed to then Runs Last
Calldelay(100, …)A pending Promiseis returnedPass a functionto seats.then(…)That functionisn't called yetconsole.log onthe next lineChecking messageprints first100ms aftercalling delayfulfilled; thefunction runs
then just holds on to the function; it doesn't call it on the spot. The function you pass runs only after the lines below have finished and the Promise is fulfilled.

If you write code that uses the result outside then, it runs before the result is settled, so the value isn't available yet. Keep the lines that use the result inside the function you pass to then, and put anything you want to do while waiting on the lines after it, outside then.

Add a Line at the End That Waits for then

The code runner on this page only collects output printed before the last line finishes. In a browser or Node.js, the then function runs even if nothing waits for it, but here you add await delay(1000); at the end to wait one second. await (syntax that doesn't move on to the next line until a Promise's result is settled) is covered in the next article.

Show a notification when each of two videos finishes converting. delay is already declared.

① In convertVideo, return a Promise that's fulfilled after ms milliseconds with a message in the form "Finished converting intro.mp4".

② Convert intro.mp4 in 150ms and outro.mp4 in 80ms, and print each message as it arrives.

③ Below ②, print "You can keep editing while it converts".

(If it runs correctly, an explanation will appear.)

JavaScript / TypeScript Editor

Run code to see output

Handling Failure — reject / catch / finally

Verifying a card PIN fails when the number is wrong. If all you have is resolve, a failure arrives as an ordinary value just like a success, so the receiving function needs an if every time to tell them apart.

The function passed to new Promise can also receive reject as its second parameter. Calling it, as in reject(new Error("message")), makes the Promise rejected (failed). The function passed to catch receives that Error when the Promise is rejected, and the function passed to finally is called last, whether it succeeded or failed.

function verifyPin(pin) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (pin === "4821") {
        resolve("PIN verified");
      } else {
        // Signal a failure by passing an Error to reject
        reject(new Error("Incorrect PIN"));
      }
    }, 100);
  });
}

// then, catch, and finally can be chained one after another (the next section explains how)
verifyPin("0000")
  .then((message) => console.log(message))        // not called, since it's rejected
  .catch((error) => console.log(error.message))   // Incorrect PIN
  .finally(() => console.log("Cleared the input field"));
  // Cleared the input field (prints last, on success or failure)
How resolve and reject Decide the State
Created with newPromise (pending)Pass a valueto resolvePass an Errorto rejectCall neitherresolve nor rejectBecomesfulfilledBecomesrejectedStayspendingCalls then,then finallyCalls catch,then finallythen, catch, andfinally never run
In the left two columns, finally is called after then or catch. Which functions are called depends on whether resolve or reject was called.

When a Promise is rejected, the then functions chained before catch are skipped, and the catch function after them is called. This catch is a Promise method, separate from the try / catch statement. If you never call either resolve or reject, the code appears to hang with no output and no error.

Look up a gift card balance. delay, balances, and codes are already declared.

① In checkGiftCard, if the code doesn't start with "GIFT-", reject it right away, without waiting, with an Error whose message is "Invalid code format".

② Otherwise, resolve after 100ms with a message in the form "Balance: 3000 yen".

③ Look up each code in codes and print the resulting message.

④ Whether it succeeds or fails, print "Hid the lookup indicator" at the end.

JavaScript / TypeScript Editor

Run code to see output

Running Steps in Order — Chaining then and return

Say that after placing an order, you want to send a confirmation email using the order number that comes back. If you write the second request inside the first then, and the third inside that one, the indentation gets deeper with every step and the code gets hard to read.

then returns a new Promise, so you can link calls as in .then(...).then(...). This is called a chain (attaching the next then to the Promise the previous then returns). A value you return from a then function is received by the next then function.

// delay is the same as in the first section's code
const placeOrder = () => delay(100, "A-2046");
const sendMail = (orderId) => delay(100, `Sent a confirmation email for ${orderId}`);

placeOrder()
  .then((orderId) => {
    console.log(`Order number: ${orderId}`);   // Order number: A-2046
    return sendMail(orderId);                 // the chain waits until the email is sent
  })
  .then((result) => {
    console.log(result);                      // Sent a confirmation email for A-2046
    return "All steps complete";              // the value goes straight to the next function
  })
  .then((status) => console.log(status));     // All steps complete
Without return, then Doesn't Wait
returnsendMail(orderId)Waits until theemail is sentresult holdsthe send resultPrints doneafter sendingJust callssendMail(orderId)Next then runswithout waitingresult isundefinedPrints donebefore sending
Only the top row waits for the email to be sent before calling the next function. The next then waits for the result only when you return the Promise.

Without return, the function returns undefined, so the next then runs without waiting for the email to be sent, and result is undefined. No error appears; the send result is simply lost, which makes this mistake easy to miss.

Once You Add Braces, You Can't Leave Out return

An arrow function without braces, like .then((id) => sendMail(id)), returns the Promise from sendMail as-is, so the next then waits. If you wrap the body in braces to add a log line partway through, add return back, as in return sendMail(id);.

Pay in yen for a $12 item from an overseas shop. delay, dollars, loadRate, and chargeCard are already declared.

① Use the rate to convert dollars to yen, print "Converted amount: 1800 yen", and pass the amount on.

② Charge that amount, and don't move on until the result is settled.

③ Print the payment result.

④ If anything fails along the way, print "Payment failed: " followed by the Error's message.

JavaScript / TypeScript Editor

Run code to see output

Waiting for Several Results Together — Promise.all

Say that when the admin dashboard opens, you request today's sales, order count, and inquiry count separately, and show the summary panel once all three are in. If you attach a separate then to each of the three Promises, the one that arrives last differs every time, so there's no single place to check that they've all arrived.

Call the three functions and pass the resulting Promises to Promise.all (a method that takes an array of Promises and waits until all their results are in), and you receive all three results at once as an array. If even one is rejected, it goes to catch instead of then.

// delay is the same as in the first section's code
const loadSales = () => delay(100, 184000);
const loadOrders = () => delay(50, 42);
const loadInquiries = () => delay(80, 3);

// All three start as soon as the functions are called. Promise.all waits until they're all in
Promise.all([loadSales(), loadOrders(), loadInquiries()])
  // Receive the result array by destructuring it by position
  .then(([sales, orders, inquiries]) => {
    // Received in the order written in the array, not the order they finished
    console.log(`Sales: ${sales} yen`);          // Sales: 184000 yen
    console.log(`Orders: ${orders}`);            // Orders: 42
    console.log(`Inquiries: ${inquiries}`);      // Inquiries: 3
  })
  .catch((error) => console.log(error.message));
The Order Passed In Isn't the Order They Finish
Index 0loadSales()184000 at 100msfinishes 3rdIndex 1loadOrders()42 at 50msfinishes 1stIndex 2loadInquiries()3 at 80msfinishes 2nd
The three finish at 50ms, 80ms, and 100ms, in that order. then receives [184000, 42, 3], in the order they were passed in (left column).

Promise.all itself doesn't start any requests; it only waits until they're all in. The table below shows, for each state of the Promises passed in, the Promise that Promise.all returns and which function gets called.

State of the Promises passed inPromise returned by Promise.allFunction called and value received
All fulfilledFulfilled once the last one is fulfilledthen receives an array in the order passed in
One is rejectedRejected as soon as that one is rejectedcatch receives that Error
Two or more are rejectedRejected as soon as the first one is rejectedcatch receives only the first Error
The rest are fulfilled, but one stays pendingStays pendingNeither then nor catch is called

Get a combined quote for a trip. The four functions whose names start with quote are already declared.

① In estimateTrip, wait until all the Promises it receives are in.

② Print the total of the prices as "Total: 68500 yen".

③ If even one fails, print "Couldn't get a quote: " followed by the Error's message.

④ Get a quote once with the flight, hotel, and rental car, then again with the rental car swapped for quoteFullCar, which is fully booked.

JavaScript / TypeScript Editor

Run code to see output
QUIZ

Knowledge Check

Answer each question one by one.

Q1If delay(100, "A").then((v) => console.log(v)); is followed by console.log("B");, in what order is the output printed?

Q2If you chain .then(A).catch(B).finally(C) onto a Promise that gets rejected, which ones are called?

Q3What array does the then of Promise.all([delay(100, "A"), delay(50, "B")]) receive?