๐ŸŽ“ A1Learner
๐Ÿ”
0 of 37 lessons completed

TypeScript Promise Combinators

Promise combinators run several promises and merge their results into one typed value. Each combinator keeps a different part of the member types, and the merged type is visible at every read.

Promise.all keeps the tuple

Three promises of three different types go in as one list. Each read position keeps the type of the promise at that position.

async function main(): Promise<void> {
  const results = await Promise.all([Promise.resolve(42), Promise.resolve('ready'), Promise.resolve(true)]);
  const count: number = results[0];
  const label: string = results[1];
  const flag: boolean = results[2];
  console.log(count);
  console.log(label);
  console.log(flag);
}

void main();

allSettled entries narrow by status

One member resolves and one rejects, and allSettled reports both. The status check narrows each entry before its payload is read.

async function main(): Promise<void> {
  const settled = await Promise.allSettled([Promise.resolve(7), Promise.reject(new Error('bad input'))]);

  for (const entry of settled) {
    if (entry.status === 'fulfilled') {
      console.log('value: ' + entry.value);
    } else {
      console.log('reason: ' + entry.reason.message);
    }
  }
}

void main();

Promise.race keeps the union

Two promises of different types race, and both are already resolved. The first member wins, and the typeof check narrows the union before the read.

async function main(): Promise<void> {
  const winner = await Promise.race([Promise.resolve('first'), Promise.resolve(2)]);

  if (typeof winner === 'string') {
    console.log('text winner: ' + winner);
  } else {
    console.log('number winner: ' + winner);
  }
}

void main();

A read at the wrong position is refused

The second tuple position holds a string, so storing it in a number variable breaks the tuple contract. The refusal below is the real compiler output for this code.

async function main(): Promise<void> {
  const pair = await Promise.all([Promise.resolve(42), Promise.resolve('ready')]);
  const label: number = pair[1];
  console.log(label);
}

void main();

Example

async function main(): Promise<void> {
  const pair = await Promise.all([Promise.resolve('Ada'), Promise.resolve(36)]);
  console.log(pair[0] + ' is ' + pair[1]);

  const settled = await Promise.allSettled([Promise.resolve('saved'), Promise.reject(new Error('offline'))]);
  for (const entry of settled) {
    if (entry.status === 'fulfilled') {
      console.log('done: ' + entry.value);
    } else {
      console.log('failed: ' + entry.reason.message);
    }
  }

  const fastest = await Promise.race([Promise.resolve('ping'), Promise.resolve('pong')]);
  console.log('race: ' + fastest);
}

void main();

Key points

  • Promise.all waits for every member and gives a tuple whose positions keep the member types in order.
  • Promise.allSettled never rejects, and each entry carries a status field with its matching payload field.
  • Checking the status field narrows an allSettled entry to the fulfilled or the rejected shape.
  • Promise.race settles with the first finished member, so its result type is the union of the member types.

In this example, one main function merges a tuple, two settled entries, and a race result, and each printed line follows the merged type of its combinator.

Note

Every promise in this lesson resolves immediately, so the merge order is fixed and the output is stable.

Try it Yourself

Edit the code, then press Run.

Running is simulated ยท TypeScript Playground

Exercise

What type does Promise.all give for a list holding a number promise and a string promise?