# Promises and async/await

> Source: https://learn-javascript.org/promises-and-async-await/
> Part of Learn JavaScript, free to read.

JavaScript is single-threaded. Anything slow — a network request, a file read, a timer — has to happen without blocking, and a `Promise` is the object representing a value that is not ready yet.

```javascript
const promise = fetch("/api/users/1");   // a Promise, immediately
```

A promise is **pending**, then either **fulfilled** with a value or **rejected** with a reason. Once settled it never changes.

## async/await

```javascript
async function getUser(id) {
  const res = await fetch(`/api/users/${id}`);
  if (!res.ok) throw new Error(`HTTP ${res.status}`);
  return res.json();
}

const user = await getUser("1");
```

`await` pauses the function until the promise settles, then gives you the value. Two rules:

- An `async` function **always** returns a promise, even if you return a plain value.
- `await` only works inside an `async` function, or at the top level of a module.

```javascript
async function answer() { return 42; }
answer();              // Promise { 42 }, not 42
await answer();        // 42
```

## Error handling

```javascript
try {
  const user = await getUser("1");
  console.log(user.email);
} catch (err) {
  console.error("failed:", err.message);
} finally {
  setLoading(false);        // runs either way
}
```

A rejected promise inside `await` throws, so `try/catch` works exactly as it does for synchronous code. That is the main reason `async/await` replaced `.then()` chains.

:::warn `fetch` does not reject on a 404
```javascript
const res = await fetch("/api/missing");   // resolves fine
```
`fetch` only rejects on a *network* failure. A 404 or 500 is a successful HTTP exchange as far as it is concerned. You must check `res.ok` yourself — omitting it is one of the most common bugs in generated JavaScript.
:::

## Concurrency

The difference that most affects real performance:

```javascript
// SEQUENTIAL — three round trips, one after another
const user = await getUser(id);
const orders = await getOrders(id);
const prefs = await getPrefs(id);

// CONCURRENT — all three at once
const [user, orders, prefs] = await Promise.all([
  getUser(id), getOrders(id), getPrefs(id),
]);
```

Both are correct. The second takes a third of the time. **Look for consecutive `await` lines with no data dependency** — that is the signal.

The same problem in a loop:

```javascript
// 100 sequential requests
for (const id of ids) {
  results.push(await fetchUser(id));
}

// all at once
const results = await Promise.all(ids.map(fetchUser));
```

### But do not go unbounded

```javascript
await Promise.all(ids.map(fetchUser));    // with 50,000 ids: a 429 storm
```

When the array size comes from data rather than from your source code, bound the concurrency:

```javascript
async function mapLimit(items, limit, fn) {
  const out = new Array(items.length);
  let i = 0;
  await Promise.all(Array.from({ length: Math.min(limit, items.length) }, async () => {
    while (i < items.length) {
      const idx = i++;
      out[idx] = await fn(items[idx], idx);
    }
  }));
  return out;
}

const results = await mapLimit(ids, 8, fetchUser);
```

## The four combinators

```javascript
Promise.all([...])         // all succeed, or reject on the first failure
Promise.allSettled([...])  // never rejects; reports each outcome
Promise.race([...])        // first to settle, success or failure
Promise.any([...])         // first to SUCCEED; rejects only if all fail
```

`allSettled` is the one people under-use. For "fetch these ten things and show me what you got", `all` is wrong — one failure discards nine successes.

```javascript
const results = await Promise.allSettled(ids.map(fetchUser));
for (const r of results) {
  if (r.status === "fulfilled") console.log(r.value.email);
  else console.error(r.reason);
}
```

## The floating promise

The most common bug in generated JavaScript:

```javascript
async function save(user) {
  db.write(user);          // not awaited
  return { ok: true };
}
```

The function returns before the write happens, any error becomes an unhandled rejection, and since Node 15 an unhandled rejection **terminates the process**.

Two variants worth recognising:

```javascript
// forEach ignores the returned promise entirely
items.forEach(async (item) => { await process(item); });
console.log("done");        // prints immediately; nothing is done

// a promise is always truthy
if (isReady()) { }          // isReady is async — this is always true
```

Turn on the rules that catch these:

```javascript
"@typescript-eslint/no-floating-promises": "error",
"@typescript-eslint/no-misused-promises": "error",
```

They need type information, which you can get on plain JavaScript with a `jsconfig.json` containing `checkJs: true`. That one file is worth it for this rule alone.

## Creating a promise

Usually you do not — you get promises from APIs. The exception is wrapping an old callback API:

```javascript
function delay(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

await delay(1000);
```

In Node, `util.promisify` or the `node:fs/promises` style APIs already exist for most of the standard library, so reach for those first.

## Cancellation

```javascript
const controller = new AbortController();
setTimeout(() => controller.abort(), 5000);

try {
  const res = await fetch(url, { signal: controller.signal });
} catch (err) {
  if (err.name === "AbortError") console.log("cancelled");
  else throw err;
}
```

`AbortSignal.timeout(5000)` does the same in one line. Threading a signal through anything that talks to a network is what makes it cancellable — and on a server, what stops you doing work for a client that has already left.

## Exercise

```javascript
// Given getUser(id), getOrders(id) and getPrefs(id) (all async):
// 1. Write loadDashboard(id) fetching all three CONCURRENTLY,
//    returning { user, orders, prefs }.
// 2. Handle failure so one broken call does not lose the other two.
// 3. Add a 3-second timeout to the whole thing.
console.log("start");
```

## Common questions

### `.then()` or `async/await`?

`async/await` for almost everything — it reads linearly and errors work with ordinary `try/catch`. `.then()` still suits short transformations and places where you cannot be in an async function. Do not mix both styles in one function.

### Why does my `try/catch` not catch the error?

Most likely the promise was not awaited, so the error happened after your `try` block finished. Errors thrown inside a callback that runs later — `setTimeout`, an event handler — also cannot be caught by the surrounding `try`, because that stack is gone by then.

### Is `await` in a loop always wrong?

No. It is right when each iteration depends on the previous one, or when you are deliberately rate-limiting. It is wrong when the iterations are independent, which is the common case — and that is why `no-await-in-loop` is best set as a warning rather than an error.
