Promise MergeLoading saved progress…

Promise Merge

Implement promiseMerge(p1, p2, merger). It takes two promises and a merger function, waits for both promises to fulfill, calls merger(value1, value2), and resolves with whatever the merger returns. If either input promise rejects, the result rejects with the first rejection reason — even if the other promise is still pending. This is the smallest useful brick in the world of promise composition: two parallel inputs, one combined output.

The point of the exercise is to internalize parallel waiting: both promises must be in flight at the same time, not chained one after the other. A naive .then chain looks like it works on small inputs and silently doubles your wall-clock time on real ones. You're also practicing the merger-as-projection pattern — separating the coordination of two async sources from the logic that combines their results — which is the building block behind Promise.all, Promise.race, and every async-fan-in helper in libraries like RxJS.

Signature

// p1, p2: promises (or plain values) whose results you want to combine.
// merger: pure function called with both fulfilled values, in (v1, v2) order.
// Returns a Promise that fulfils with merger's return value, or rejects with
// the first rejection reason from either input — or with merger's own error.
function promiseMerge<A, B, R>(
  p1: A | PromiseLike<A>,
  p2: B | PromiseLike<B>,
  merger: (v1: A, v2: B) => R | PromiseLike<R>,
): Promise<R>;

Examples

// Basic addition. Both inputs fulfil, merger is called with (1, 2).
promiseMerge(Promise.resolve(1), Promise.resolve(2), (a, b) => a + b)
  .then(console.log); // 3
// String concat. Merger projects two values into one string.
const user  = Promise.resolve('Ada');
const greet = Promise.resolve('hello');
promiseMerge(user, greet, (u, g) => `${g}, ${u}`)
  .then(console.log); // 'hello, Ada'
// First input rejects — result rejects immediately with that reason,
// even if p2 would have resolved later.
const bad  = Promise.reject('boom');
const slow = new Promise((r) => setTimeout(() => r('late'), 100));
bad.catch(() => {});
promiseMerge(bad, slow, (a, b) => [a, b]).catch(console.log); // 'boom'
// Second input rejects first — its reason wins.
const slowOk = new Promise((r) => setTimeout(() => r('ok'), 50));
const fastBad = Promise.reject('fail');
fastBad.catch(() => {});
promiseMerge(slowOk, fastBad, (a, b) => a + b).catch(console.log); // 'fail'

Notes

  • Parallel, not sequential. Both promises must run at the same time. Awaiting p1 and then p2 is the wrong shape — it doubles the wall-clock wait.
  • Merger order is fixed. The merger is always called with (value1, value2) matching the argument order, regardless of which promise settled first.
  • First rejection wins. If both inputs reject, the result rejects with whichever reason arrives first; the second is dropped.
  • Merger errors propagate. If merger throws synchronously or returns a rejected promise, the result rejects with that error. Don't swallow it.
  • Plain values are OK. promiseMerge(1, 2, (a, b) => a + b) should work — wrap inputs with Promise.resolve.
  • Return a real Promise. Tests check instanceof Promise. Don't return a thenable.
Loading editor…