using Promise.all and Promise.allSettled like a boss (in a semantically correct way)

Sometimes we see this pattern with Promise.all

const [api1, api2, api3, api4] = await Promise.all([
  getForApiOne(id),
  getForApiTwo(id),
  getForApiThree(id).catch(() => undefined),
  getForApiFour(id).catch(() => undefined),
]);

This code works. But its circumventing the meaning of Promise.all(). 
Promise.all() stops if any promise fails (because Promise.all() assumes there is a strict dependency between promises). But this code catches 2 failed exception and returns undefined, effectively "working around" Promise.all().
Noting: it does retain the "fail fast" behavior of Promise.all()

What would be more explicit is to use a mix of Promise.all() for critical and Promise.allSettled() for non critical calls because it's more explicit for what you're trying to achieve. 
Use Promise.allSettled() when you want to allow all promises to run to completion, even if some fail, which is what is the intent in original code.

note: Promise.allSettled() does return a different structure than Promise.all() which you need to check to extract the value i.e. in case of "status: rejected", use undefined as value to match current code.

We can refactor the calls like so. It's some extra code but now the meaning is clear and we're using language features with clear intent. We're also starting all requests in parallel as before.

```
// 1. Kick off ALL promises simultaneously so they run in parallel
const p1 = getForApiOne(id);
const p2 = getForApiTwo(id);
const p3 = getForApiThree(id);
const p4 = getForApiFour(id);

// 2. Await critical data. If any of these fail, it fails fast immediately.
const [api1, api2] = await Promise.all([p1, p2]);

// 3. Await optional data where we tolerate failures.
// Notice we dropped the inline .catch() blocks!
const [api3Result, api4Result] = await Promise.allSettled([p3, p4]);

// 4. Extract the values safely using the proper allSettled structure
const apiCount = api3Result.status === 'fulfilled' ? api3Result.value?.count : undefined;
const apiMetrics = api4Result.status === 'fulfilled' ? api4Result.value?.metrics : undefined;
```

Comments

Popular posts from this blog

angular js protractor e2e cheatsheet

angularjs ui-router query string parameter support

Notes on interview with Kent Beck about AI and why it's like a "genie"