How does async/await work, and how do you convert callback-based APIs to promises?
3 minintermediatenodejsasync-awaitpromisifypromiseserror-handling
Quick Answer
async/await is syntactic sugar over Promises: an async function returns a Promise, and await pauses it until a promise settles, using try/catch for errors. Convert Node-style callback APIs with util.promisify, or use the built-in promise variants like fs.promises.
Detailed Answer
Answer:
async/await lets you write promise-based code that reads sequentially:
async function loadDashboard(id) {
try {
const user = await getUser(id);
const posts = await getPosts(user.id);
return { user, posts };
} catch (err) {
// any rejection above lands here
throw new Error('Failed to load dashboard', { cause: err });
}
}
- An
asyncfunction always returns a Promise. awaitsuspends the function (not the thread) until the awaited promise settles; a rejection throws, caught bytry/catch.
Converting callback APIs to promises:
util.promisifyfor error-first callback functions:
const util = require('util');
const readFile = util.promisify(require('fs').readFile);
const data = await readFile('data.txt', 'utf8');
- Use built-in promise APIs where available:
const fs = require('fs/promises');
const data = await fs.readFile('data.txt', 'utf8');
- Manual wrapping for non-standard callbacks:
function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
await delay(1000);
Common pitfalls:
- Accidental serialization: awaiting in a loop runs items one-by-one. Use
Promise.allwithmapfor parallelism. - Missing
await: forgetting it means you get a pending Promise instead of the value, and errors go unhandled. forEach+ async doesn't await — usefor...oforPromise.all(map(...)).