# Promises — Part 2: Combinators and Patterns

## 1. `Promise.all` — Wait for Everything, Fail Fast

```js
const results = await Promise.all([
  fetch("/api/users"),
  fetch("/api/orders"),
  fetch("/api/products"),
]);
```

- Resolves with an array of all results, **in the same order as the input**, once every Promise fulfills.
- Rejects **immediately** with the first rejection reason if ANY Promise rejects — the other Promises keep running in the background, but their results are discarded by `Promise.all` itself.
- Use when you need **all** results and can't proceed with partial data.

## 2. `Promise.allSettled` — Wait for Everything, Never Fails

```js
const results = await Promise.allSettled([
  fetch("/api/users"),
  fetch("/api/might-fail"),
]);
// [{ status: "fulfilled", value: ... }, { status: "rejected", reason: ... }]
```

Always resolves (never rejects) once every input Promise has settled, reporting each outcome individually. Use when partial failures are acceptable and you want to know about all of them.

## 3. `Promise.race` — First to Settle Wins

```js
const result = await Promise.race([
  fetch("/api/data"),
  new Promise((_, reject) => setTimeout(() => reject(new Error("timeout")), 5000)),
]);
```

Settles (fulfilled OR rejected) as soon as the **first** input Promise settles, in either direction. The classic use case: implementing a timeout by racing a real operation against a timer that rejects.

## 4. `Promise.any` — First to Fulfill Wins

```js
const result = await Promise.any([
  fetch("https://mirror1.example.com/data"),
  fetch("https://mirror2.example.com/data"),
]);
```

Resolves with the first **fulfillment**, ignoring rejections — only rejects if **all** input Promises reject (with an `AggregateError` containing all reasons). Use for "try several sources, take whichever succeeds first."

## 5. Comparison Table

| Combinator | Resolves when | Rejects when |
|---|---|---|
| `Promise.all` | all fulfill | any one rejects (immediately) |
| `Promise.allSettled` | all settle (never rejects) | never |
| `Promise.race` | first settles (fulfilled OR rejected) | first settles as a rejection |
| `Promise.any` | first fulfills | all reject |

## 6. Turning a Callback API into a Promise (Promisifying)

```js
function readFilePromise(path) {
  return new Promise((resolve, reject) => {
    fs.readFile(path, "utf8", (err, data) => {
      if (err) reject(err);
      else resolve(data);
    });
  });
}
```

This exact wrapping pattern — resolve on success, reject on the error-first callback's error — is how legacy callback-based APIs are adapted to work with `async`/`await` (Node also provides `util.promisify` to automate it for standard error-first functions).

## 7. Common Pitfalls

1. **Using `Promise.all` when partial failure is acceptable** — one rejection loses all other results; use `allSettled` instead if you need to know about every outcome.
2. **Forgetting `Promise.all` runs everything CONCURRENTLY, not sequentially** — all input Promises start immediately when the array is created, regardless of `await`; sequencing must be done explicitly if needed (see the Parallel vs Sequential topic).
3. **Not handling rejection in a "fire and forget" Promise** — an unhandled rejected Promise triggers an `unhandledRejection` warning/crash in Node and browsers; always attach a `.catch()` or wrap in `try/catch` with `await`.
4. **Wrapping an already-async function in `new Promise()` unnecessarily** — if a function already returns a Promise, just return it directly; wrapping it again ("Promise constructor antipattern") adds needless indirection and can swallow errors if done incorrectly.

## 8. Quick Reference

```
Promise.all([...])         → all-or-nothing, fails fast
Promise.allSettled([...])   → always resolves, per-item status
Promise.race([...])          → first to settle, either way
Promise.any([...])            → first to succeed, ignores failures until all fail
```
