---
name: async-await-patterns
description: Use when writing JavaScript or TypeScript code with asynchronous operations
---
Senior JavaScript/TypeScript Engineer. Reputation depends on production-grade asynchronous code. Prevents race conditions, memory leaks, and unhandled promise rejections through disciplined async patterns.
You MUST use async/await for ALL asynchronous operations instead of raw promises, callbacks, or blocking patterns. This is critical to application stability. This is NOT optional. This is NOT negotiable.
## Invariant Principles
1. **Explicit async boundary**: Function containing await MUST be marked async. Compiler enforces; no exceptions.
2. **Await ALL promises**: Every promise-returning call requires await. Missing await = bug (returns Promise, not value).
3. **Structured error handling**: try-catch wraps async operations. Unhandled rejections crash applications.
4. **Pattern consistency**: async/await XOR promise chains. Never mix in same function.
5. **Parallelism via combinators**: Independent operations use Promise.all/allSettled. Sequential only when dependencies exist.
## Required Reasoning
Before writing ANY async code, verify step-by-step:
1. Is this operation asynchronous? (API calls, file I/O, timers, database queries)
2. Did I mark the containing function as `async`?
3. Did I use `await` for every promise-returning operation?
4. Did I add proper try-catch error handling?
5. Did I avoid mixing async/await with `.then()/.catch()`?
6. Can independent operations run in parallel with Promise.all?
Now write asynchronous code following this checklist.
## Core Pattern
```typescript
async function operationName(): Promise {
try {
const result = await asyncOperation();
return result;
} catch (error) {
// Handle or rethrow with context
throw error;
}
}
```
## Forbidden Patterns: Quick Reference
| Anti-pattern | Fix |
|--------------|-----|
| `.then()/.catch()` chains | async/await with try-catch |
| `const x = asyncFn()` (missing await) | `const x = await asyncFn()` |
| `function` with await inside | `async function` |
| Await without try-catch | Wrap in try-catch |
| Mix async/await + .then() | Pure async/await |
| Callbacks when promises available | async/await |
| Sequential awaits for independent ops | Promise.all |
## Forbidden Patterns: Detailed Examples
### Raw Promise Chains Instead of Async/Await
```typescript
// BAD - Using .then()/.catch() chains
function fetchData() {
return fetch('/api/data')
.then(response => response.json())
.then(data => processData(data))
.catch(error => handleError(error));
}
// CORRECT - Using async/await
async function fetchData() {
try {
const response = await fetch('/api/data');
const data = await response.json();
return processData(data);
} catch (error) {
handleError(error);
throw error;
}
}
```
### Forgetting await Keyword
```typescript
// BAD - Missing await (returns Promise instead of value)
async function getData() {
const data = fetchFromDatabase(); // Forgot await!
return data.id; // Error: data is a Promise
}
// CORRECT - Using await
async function getData() {
const data = await fetchFromDatabase();
return data.id;
}
```
### Missing async Keyword on Function
```typescript
// BAD - Using await without async
function loadUser() {
const user = await database.getUser(); // SyntaxError!
return user;
}
// CORRECT - Mark function as async
async function loadUser() {
const user = await database.getUser();
return user;
}
```
### Missing Error Handling
```typescript
// BAD - No try-catch for async operations
async function saveData(data) {
const result = await database.save(data);
return result; // Unhandled promise rejection if save fails!
}
// CORRECT - Proper error handling
async function saveData(data) {
try {
const result = await database.save(data);
return result;
} catch (error) {
console.error('Save failed:', error);
throw new Error('Failed to save data');
}
}
```
### Mixing Async/Await with Promise Chains
```typescript
// BAD - Inconsistent pattern mixing
async function processUser() {
const user = await getUser();
return updateUser(user)
.then(result => result.data)
.catch(error => console.error(error));
}
// CORRECT - Consistent async/await
async function processUser() {
try {
const user = await getUser();
const result = await updateUser(user);
return result.data;
} catch (error) {
console.error(error);
throw error;
}
}
```
## Parallel vs Sequential
```typescript
// PARALLEL: independent operations
const [a, b, c] = await Promise.all([fetchA(), fetchB(), fetchC()]);
// SEQUENTIAL: each depends on previous
const inventory = await checkInventory();
const payment = await processPayment(inventory);
const order = await createOrder(payment);
// FAULT-TOLERANT: continue despite failures
const results = await Promise.allSettled([op1(), op2(), op3()]);
// Each result: { status: 'fulfilled', value } or { status: 'rejected', reason }
```
## Complete Real-World Example
```typescript
async function updateUserProfile(userId: string, updates: ProfileUpdates): Promise {
try {
const user = await database.users.findById(userId);
if (!user) {
throw new Error(`User ${userId} not found`);
}
const validatedUpdates = await validateProfileData(updates);
const updatedUser = await database.users.update(userId, validatedUpdates);
// Parallel operations for notifications
await Promise.all([
notificationService.send(userId, 'Profile updated'),
auditLog.record('profile_update', { userId, updates: validatedUpdates })
]);
return updatedUser;
} catch (error) {
if (error instanceof ValidationError) {
throw new BadRequestError('Invalid profile data', error);
}
if (error instanceof DatabaseError) {
throw new ServiceError('Database operation failed', error);
}
throw new Error(`Failed to update profile: ${error.message}`);
}
}
```
Demonstrates: async keyword, await on every async operation, comprehensive try-catch, proper error types, parallel operations with Promise.all, consistent async/await throughout.
## Inputs
| Input | Required | Description |
|-------|----------|-------------|
| Code with async operations | Yes | JavaScript/TypeScript code needing async handling |
| Dependency graph | No | Which operations depend on others (determines parallel vs sequential) |
## Outputs
| Output | Type | Description |
|--------|------|-------------|
| Async code | Inline | Properly structured async/await code |
| Error handling strategy | Inline | try-catch blocks with typed error handling |
## Self-Check
Before submitting ANY asynchronous code, verify:
- [ ] Did I mark the function as `async`?
- [ ] Did I use `await` for EVERY promise-returning operation?
- [ ] Did I wrap await operations in try-catch blocks?
- [ ] Did I avoid using .then()/.catch() chains?
- [ ] Did I avoid mixing async/await with promise chains?
- [ ] Did I avoid using callbacks when async/await is available?
- [ ] Did I consider whether operations can run in parallel with Promise.all()?
- [ ] Did I provide meaningful error messages in catch blocks?
- [ ] Does error handling preserve error context?
If NO to ANY item above: STOP. Rewrite using proper async/await before proceeding.
You MUST use async/await for ALL asynchronous operations. NEVER use raw promise chains when async/await is clearer. NEVER forget the await keyword. NEVER omit error handling. This is critical to code quality and application stability. This is non-negotiable.