# Async/await used consistently

- **Pattern:** `ab-002163` (`code-quality-essentials.organization.async-consistency`)
- **Severity:** low
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-002163
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-002163)

## Why it matters

Mixing `.then()` chains and `async/await` in the same codebase — or in the same function — creates inconsistent error propagation: `.then()` chains require `.catch()` while `async/await` uses `try/catch`, and mixing them means some rejections are caught by one mechanism and some by the other. ISO 25010:2011 §6.5.2 classifies this as a maintainability defect. Nested `.then()` chains also re-introduce the callback-pyramid problem that `async/await` was designed to solve, making control flow harder to read and independent async operations sequential when they could run in parallel.

## Severity rationale

Low because mixed async styles do not cause immediate failures but produce inconsistent error handling that leads to unhandled rejections and makes the control flow significantly harder to read and maintain.

## Remediation

Standardize on `async/await` throughout. Use `Promise.all()` for genuinely independent async operations — never `await` them sequentially when they don't depend on each other's results.

```typescript
// Bad: nested .then() chains, mixed with async
async function loadUserData(id: string) {
  return fetchUser(id)
    .then((user) => fetchUserPosts(user.id)
      .then((posts) => ({ user, posts })))
    .catch((err) => console.error(err))
}

// Good: consistent async/await
async function loadUserData(id: string) {
  try {
    const user = await fetchUser(id)
    const posts = await fetchUserPosts(user.id)
    return { user, posts }
  } catch (err) {
    logger.error('Failed to load user data', err)
    throw err
  }
}

// Good: parallel operations with Promise.all
async function loadDashboard(userId: string) {
  const [user, posts] = await Promise.all([
    fetchUser(userId),
    fetchUserPosts(userId),
  ])
  return { user, posts }
}
```

## Detection

- **ID:** `async-consistency`
- **Severity:** `low`
- **What to look for:** Scan source files for async patterns. Look for mixing of `.then()/.catch()` promise chains with `async/await` in the same file or function. Also look for: nested `.then()` chains (the "callback pyramid" anti-pattern in promise form), `new Promise(resolve => resolve(value))` wrapping a value that could just be returned directly, `async` functions that don't use `await` (unnecessary async overhead), uncaught rejections from `.then()` chains without `.catch()` handlers, and mixing `Promise.all()` correctly vs sequentially awaiting independent operations. `async/await` is the preferred pattern for readability — `.then()` is acceptable for simple transformations or when chaining is genuinely cleaner, but mixing both styles in the same function is a code smell.
- **Pass criteria:** Enumerate all relevant code locations. Consistent use of `async/await` throughout. `.then()` chains used only where they genuinely improve readability (rare). `Promise.all()` used for independent parallel async operations with at least 1 verified location.
- **Fail criteria:** Same module or function mixes `.then()` chains and `async/await` without clear reason. Nested promise chains where `await` would be cleaner.
- **Skip (N/A) when:** Project has no asynchronous code.
- **Detail on fail:** `"Mixed async styles: .then() chains and async/await used in the same files; inconsistent error handling patterns"`
- **Remediation:** Refactor `.then()` chains to `async/await`:

  ```typescript
  // Bad: mixed styles, harder to read
  async function loadUserData(id: string) {
    return fetchUser(id)
      .then((user) => {
        return fetchUserPosts(user.id)
          .then((posts) => ({ user, posts }))
      })
      .catch((err) => console.error(err))
  }

  // Good: consistent async/await
  async function loadUserData(id: string) {
    try {
      const user = await fetchUser(id)
      const posts = await fetchUserPosts(user.id)
      return { user, posts }
    } catch (err) {
      logger.error('Failed to load user data', err)
      throw err
    }
  }

  // Good: parallel operations with Promise.all
  async function loadDashboard(userId: string) {
    const [user, posts, notifications] = await Promise.all([
      fetchUser(userId),
      fetchUserPosts(userId),
      fetchNotifications(userId),
    ])
    return { user, posts, notifications }
  }
  ```

## External references

- iso-25010 maintainability.analysability

Taxons: code-quality

HTML version: https://auditbuffet.com/patterns/ab-002163
