JavaScript Functions and Methods: A Topic-by-Topic Reference · Lesson 11 of 12

Lesson 11: Promises and async/await

Promises, then/catch/finally, async/await, Promise.all, race, allSettled, any, error handling, and the order in which async code runs.

Lesson 11: Promises and async/await

Some work takes time: reading a file, asking a server, waiting for a timer. A Promise represents a result that will arrive later: it is pending, then fulfilled (with a value) or rejected (with an error).

Promises

new Promise((resolve, reject) => ...)

Create a promise: call resolve with a value or reject with an error.

const wait = (ms, value) => new Promise((resolve) => setTimeout(() => resolve(value), ms));
wait(10, "done after 10 ms").then((v) => console.log(v));
console.log("this prints first");

Output:

this prints first
done after 10 ms

then, catch, finally

then receives the value; catch the error; finally runs either way. Each returns a new promise, so you can chain.

Promise.resolve(5)
  .then((n) => n * 2)
  .then((n) => { console.log("got", n); throw new Error("oops"); })
  .catch((e) => { console.log("caught", e.message); return "recovered"; })
  .finally(() => console.log("cleanup"))
  .then((v) => console.log(v));

Output:

got 10
caught oops
cleanup
recovered

async / await

async functions and await

await pauses the function until a promise settles. An async function always returns a promise. Use try/catch for errors.

const wait = (ms, v) => new Promise((r) => setTimeout(() => r(v), ms));
async function main() {
  console.log("start");
  const a = await wait(10, "A");
  const b = await wait(10, "B");
  console.log(a + b);
  try {
    await Promise.reject(new Error("failed"));
  } catch (e) {
    console.log("caught:", e.message);
  }
}
main().then(() => console.log("finished"));

Output:

start
AB
caught: failed
finished

Running several at once

Promise.all([...])

Waits for all to succeed (and gives the results in order), or fails as soon as one fails.

const wait = (ms, v) => new Promise((r) => setTimeout(() => r(v), ms));
const t0 = Date.now();
Promise.all([wait(30, "a"), wait(10, "b"), 3]).then((r) => console.log(r, Date.now() - t0 < 60));

Output:

[ 'a', 'b', 3 ] true

Promise.allSettled([...])

Waits for all, whether they succeed or fail, and reports each outcome.

Promise.allSettled([Promise.resolve(1), Promise.reject(new Error("no")), 3]).then((r) => console.log(r.map((x) => x.status)));

Output:

[ 'fulfilled', 'rejected', 'fulfilled' ]

Promise.race and Promise.any

race: the first to settle. any: the first to succeed.

const wait = (ms, v) => new Promise((r) => setTimeout(() => r(v), ms));
Promise.race([wait(30, "slow"), wait(5, "fast")]).then((v) => console.log("race:", v));
Promise.any([Promise.reject(new Error("x")), wait(10, "ok")]).then((v) => console.log("any:", v));

Output:

race: fast
any: ok

Sequential versus parallel

Awaiting in a loop runs one after another; start all first to run them together.

const wait = (ms, v) => new Promise((r) => setTimeout(() => r(v), ms));
async function run() {
  let t = Date.now();
  await wait(30); await wait(30);
  console.log("sequential >= 60ms:", Date.now() - t >= 55);
  t = Date.now();
  await Promise.all([wait(30), wait(30)]);
  console.log("parallel < 55ms:", Date.now() - t < 55);
}
run();

Output:

sequential >= 60ms: true
parallel < 55ms: true

Timeouts and retries

A timeout wrapper and a retry

Small, useful patterns built from promises.

const wait = (ms, v) => new Promise((r) => setTimeout(() => r(v), ms));
const timeout = (p, ms) => Promise.race([p, new Promise((_, rej) => setTimeout(() => rej(new Error("timeout")), ms))]);
timeout(wait(50), 10).catch((e) => console.log(e.message));

async function retry(fn, times) {
  for (let i = 1; i <= times; i++) {
    try { return await fn(i); } catch (e) { if (i === times) throw e; }
  }
}
retry(async (i) => { if (i < 3) throw new Error("fail"); return "ok on try " + i; }, 5).then(console.log);

Output:

ok on try 3
timeout

Order of execution

JavaScript runs the current code to the end first. Then microtasks (promise callbacks) run, then timers.

console.log("1 sync");
setTimeout(() => console.log("4 timer"), 0);
Promise.resolve().then(() => console.log("3 promise"));
queueMicrotask(() => console.log("3b microtask"));
console.log("2 sync");

Output:

1 sync
2 sync
3 promise
3b microtask
4 timer

Always handle rejections (catch or try/catch). An unhandled rejection can crash Node.js and hides bugs in browsers.

Try it yourself

  1. Write delay(ms) that returns a promise, and use it to print three lines one second apart.
  2. Run three delays in parallel with Promise.all and measure the time.
  3. Write a function that tries to fetch a URL and returns a default if it fails (use try/catch).

Final lesson: regular expressions, DOM essentials and handy extras.

Finished this lesson?Log in to save your progress and pick up where you left off.
Next lesson →

Comments

Loading comments…

MeroSir Team
A free MeroSir course. Run the examples yourself and try the exercises: you learn programming by doing. Spotted a mistake? Use the Report button above the lesson.
Lesson 11: Promises and async/await | MeroSir