Write a program to create a promise and an async/await function.
A Promise wraps an async operation: `new Promise((resolve, reject) => …)`. `async/await` is syntactic sugar — `async` makes a function return a Promise; `await` pauses until the awaited Promise settles. Same semantics, cleaner control flow.
Creating a Promise
const wait = (ms) => new Promise((resolve, reject) => {
if (ms < 0) reject(new Error("ms must be non-negative"));
else setTimeout(() => resolve(ms), ms);
});
wait(500)
.then((ms) => console.log("waited", ms))
.catch((err) => console.error(err));The executor function receives resolve (fulfill with value) and reject (fail with reason). The Promise can only settle once.
async / await equivalent
async function delay(ms) {
if (ms < 0) throw new Error("ms must be non-negative");
return new Promise((r) => setTimeout(r, ms));
}
async function main() {
try {
await delay(500);
console.log("done");
} catch (err) {
console.error(err);
}
}
main();async makes the function return a Promise. await unwraps a Promise to its value (or throws on rejection).
Equivalence
async function f() { return 1; }
// equivalent to
function f() { return Promise.resolve(1); }async function f() { await x; doThing(); }
// equivalent to
function f() { return Promise.resolve(x).then(() => doThing()); }Three states
A Promise is pending → fulfilled or rejected. Once settled, immutable.
Chaining
fetchUser()
.then((user) => fetchPosts(user.id))
.then((posts) => render(posts))
.catch((err) => showError(err));Each .then returns a new Promise; returning a Promise inside flattens (no nesting).
async version
async function load() {
try {
const user = await fetchUser();
const posts = await fetchPosts(user.id);
render(posts);
} catch (err) {
showError(err);
}
}Reads sequentially; easier to debug.
Parallel
const [a, b] = await Promise.all([fetchA(), fetchB()]); // both at onceDon't do await fetchA(); await fetchB(); if they're independent — that's sequential and wastes time.
Don't forget
- An
asyncfunction always returns a Promise. awaitonly insideasync(or top-level in modules).- Errors in async functions become rejected Promises.
Promise.reject(value)andthrowinside async are equivalent.- An unhandled rejection logs a warning and may crash Node.
Interview framing
"new Promise((resolve, reject) => ...) constructs a promise — call resolve or reject from inside. async makes a function return a Promise; await pauses until the awaited promise settles. They're the same primitive — async/await reads as synchronous code, easier to debug. Independent awaits should be Promise.all'd, not sequential. Errors in async functions become rejected promises — wrap with try/catch or .catch on the call site."
Follow-up questions
- •What's the difference between Promise.all and Promise.allSettled?
- •How do unhandled rejections behave?
- •When would you use Promise constructor directly?
Common mistakes
- •Sequential awaits for independent work.
- •Forgetting to return inside .then.
- •Promise constructor anti-pattern: wrapping an existing Promise.
- •Unhandled rejections.
Performance considerations
- •Microtask queue runs Promise callbacks before next macrotask — fast but can starve I/O if you spin.
Edge cases
- •Resolving with another Promise — it chains.
- •Errors thrown synchronously inside executor still reject.
- •Top-level await only in ES modules.
Real-world examples
- •Every fetch call, db.query, Node's fs.promises, browser APIs.