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()
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;
```
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
Post a Comment