skills/async-await-patterns/SKILL.md
Use when writing JavaScript or TypeScript code with asynchronous operations, fixing promise-related bugs, or converting callback/promise patterns to async/await. Triggers: 'promise chain', 'unhandled rejection', 'race condition in JS', 'callback hell', 'Promise.all', 'sequential vs parallel async', 'missing await'. Enforces async/await discipline over raw promises.
npx skillsauth add axiomantic/spellbook async-await-patternsInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
<CRITICAL_INSTRUCTION> Use async/await for ALL asynchronous operations instead of raw promises, callbacks, or blocking patterns. This is critical to application stability. NOT optional. NOT negotiable. </CRITICAL_INSTRUCTION>
async?await for every promise-returning operation?.then()/.catch()?Now write asynchronous code following this checklist. </analysis>
async function operationName(): Promise<ReturnType> {
try {
const result = await asyncOperation();
return result;
} catch (error) {
throw error; // Handle or rethrow with context
}
}
| 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 |
// BAD
function fetchData() {
return fetch('/api/data')
.then(response => response.json())
.then(data => processData(data))
.catch(error => handleError(error));
}
// CORRECT
async function fetchData() {
try {
const response = await fetch('/api/data');
const data = await response.json();
return processData(data);
} catch (error) {
handleError(error);
throw error;
}
}
</FORBIDDEN>
<FORBIDDEN pattern="2">
### Forgetting await Keyword
// BAD - returns Promise instead of value
async function getData() {
const data = fetchFromDatabase(); // Forgot await!
return data.id; // Error: data is a Promise
}
// CORRECT
async function getData() {
const data = await fetchFromDatabase();
return data.id;
}
</FORBIDDEN>
<FORBIDDEN pattern="3">
### Missing async Keyword on Function
// BAD - SyntaxError
function loadUser() {
const user = await database.getUser();
return user;
}
// CORRECT
async function loadUser() {
const user = await database.getUser();
return user;
}
</FORBIDDEN>
<FORBIDDEN pattern="4">
### Missing Error Handling
// BAD - unhandled promise rejection if save fails
async function saveData(data) {
const result = await database.save(data);
return result;
}
// CORRECT
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');
}
}
</FORBIDDEN>
<FORBIDDEN pattern="5">
### Mixing Async/Await with Promise Chains
// BAD
async function processUser() {
const user = await getUser();
return updateUser(user)
.then(result => result.data)
.catch(error => console.error(error));
}
// CORRECT
async function processUser() {
try {
const user = await getUser();
const result = await updateUser(user);
return result.data;
} catch (error) {
console.error(error);
throw error;
}
}
</FORBIDDEN>
// 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 }
async function updateUserProfile(userId: string, updates: ProfileUpdates): Promise<User> {
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);
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}`);
}
}
For Python scripts with multiple network requests or I/O operations, consider using asyncio with pipeline queues. This is not mandatory, but works well when a script naturally decomposes into producer/consumer stages:
This pattern shines when you have distinct stages (fetch, transform, write) that can overlap. For simple scripts with a handful of sequential requests, asyncio.gather or Promise.all is sufficient.
async?await for EVERY promise-returning operation?If NO to ANY item above: STOP. Rewrite using proper async/await before proceeding. </reflection>
<FINAL_EMPHASIS> 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. Non-negotiable. </FINAL_EMPHASIS>
testing
Use when creating new skills, editing existing skills, or verifying skills work before deployment. Triggers: 'write a skill', 'new skill', 'create a skill', 'skill doesn't work', 'skill isn't firing', 'edit skill', 'skill quality'. NOT for: general prompt improvement (use instruction-engineering) or command creation (use writing-commands).
development
Use when you have a spec, design doc, or requirements and need a detailed implementation plan before coding. Triggers: 'write a plan', 'create implementation plan', 'plan this out', 'break this down into steps', 'convert design to tasks', 'implementation order'. Also invoked by develop during planning. NOT for: reviewing existing plans (use reviewing-impl-plans).
testing
Use when creating new commands, editing existing commands, or reviewing command quality. Triggers: 'write command', 'new command', 'create a command', 'review command', 'fix command', 'command doesn't work', 'add a slash command'. NOT for: skill creation (use writing-skills).
development
Use when about to claim discovery during debugging. Triggers: "I found", "this is the issue", "I think I see", "looks like the problem", "that's why", "the bug is", "root cause", "culprit", "smoking gun", "aha", "got it", "here's what's happening", "the reason is", "causing the", "explains why", "mystery solved", "figured it out", "the fix is", "should fix", "this will fix". Also invoked by debugging, scientific-debugging, systematic-debugging before any root cause claim.