async/await
debt(d7/e3/b3/t7)
Closest to 'only careful code review or runtime testing' (d7). ESLint can catch some patterns (e.g., no-floating-promises with TypeScript plugin, no-await-in-loop), but many misuses — like missing try/catch, async in forEach, or not awaiting a promise — require careful review or manifest only at runtime as silent failures or crashes. The tools listed (eslint, typescript) help but don't catch all cases automatically without specific rule configuration.
Closest to 'simple parameterised fix' (e3). The quick_fix indicates wrapping in try/catch, swapping sequential awaits for Promise.all(), and adding unhandledRejection handlers — these are small, targeted fixes within one function or component, not one-liners but not cross-cutting refactors either.
Closest to 'localised tax' (b3). async/await applies to web and cli contexts broadly, but individual misuses are typically localised to specific functions or modules. The pattern doesn't impose a systemic architectural burden on the whole codebase — fixing one async function rarely forces changes across the system.
Closest to 'serious trap' (t7). The misconception field explicitly states that developers believe async/await makes JavaScript 'truly synchronous' — a fundamental misunderstanding about event loop behavior. Common mistakes like async in forEach, missing awaits, and sequential vs. parallel execution all stem from this wrong mental model, which contradicts how similar synchronous constructs behave in other languages.
Also Known As
TL;DR
Explanation
async/await (ES2017) makes asynchronous code read like synchronous code. An async function implicitly returns a Promise — returning a value resolves it, throwing rejects it. await can only appear inside an async function and pauses its execution until the awaited Promise settles, without blocking the event loop. Error handling uses try/catch identically to synchronous code. Common pitfalls: awaiting in a loop sequentially (use Promise.all() for parallel execution), forgetting await (function returns a Promise, not the value), and unhandled promise rejections. Top-level await is available in ES modules. In PHP, the conceptual equivalent is Fiber suspension in Revolt/Amp.
Diagram
sequenceDiagram
participant CALL as Caller
participant ASYNC as async function
participant IO as I/O (fetch, DB)
CALL->>ASYNC: call fetchUser(id)
ASYNC->>IO: await fetch('/api/user')
Note over ASYNC,IO: Fiber suspended - main thread free
IO-->>ASYNC: response
ASYNC->>IO: await response.json()
IO-->>ASYNC: user data
ASYNC-->>CALL: return user
Note over CALL,IO: await unwraps Promise<br/>try/catch handles rejection
Common Misconception
Why It Matters
Common Mistakes
- Not awaiting a Promise — the function continues before the async operation completes.
- Sequential awaits when operations are independent — use Promise.all() for parallel execution.
- Not wrapping await in try/catch — unhandled rejections crash Node.js processes.
- async functions in array methods like forEach — forEach doesn't await the callbacks.
Code Examples
// Sequential awaits — takes 3 seconds when 1 would suffice:
async function loadDashboard() {
const user = await fetchUser(); // 1s
const orders = await fetchOrders(); // 1s — could run in parallel!
const stats = await fetchStats(); // 1s — could run in parallel!
}
// Parallel:
const [user, orders, stats] = await Promise.all([fetchUser(), fetchOrders(), fetchStats()]);
// Sequential (slow) vs parallel (fast)
const [a, b] = await Promise.all([fetchA(), fetchB()]);
// Error handling
try {
const data = await fetch('/api').then(r => r.json());
} catch (err) {
console.error('Failed:', err);
}