Skip to main content

Async/Await

Introduction​

Callbacks made async possible, but quickly led to callback hell. Promises fixed much of this, but chaining many .then() calls can still feel awkward.

To make async code look and feel like synchronous code, ES2017 introduced async/await.

  • An async function always returns a promise.
  • Inside an async function, you can use await to pause execution until a promise is resolved or rejected.
  • This makes asynchronous code much easier to read and maintain.

Concepts​

  • async keyword: marks a function as asynchronous.
  • It automatically wraps return values in a promise.
  • await keyword: pauses the function until the promise resolves, then returns its result.
  • Error handling: use try...catch instead of `.catch().
  • Sequential vs parallel: multiple awaits in sequence wait one after another; use Promise.all to run them in parallel.

Examples​

Basic Example

function fetchData() {
return new Promise(resolve => {
setTimeout(() => resolve({ id: 1, name: "Alice" }), 2000);
});
}

async function getUser() {
console.log("Fetching user...");
const user = await fetchData(); // waits here
console.log("User received:", user);
}

getUser();

Error Handling

function fetchWithError() {
return new Promise((resolve, reject) => {
setTimeout(() => reject("Failed to fetch"), 1000);
});
}

async function run() {
try {
const data = await fetchWithError();
console.log(data);
} catch (err) {
console.error("Error caught:", err);
} finally {
console.log("Done");
}
}

run();

Sequential vs Parallel

function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}

async function sequential() {
console.log("Start sequential");
await delay(1000);
console.log("After 1s");
await delay(1000);
console.log("After 2s");
}

async function parallel() {
console.log("Start parallel");
await Promise.all([delay(1000), delay(1000)]);
console.log("Both finished after 1s");
}

Challenges​

1. Rewrite promise with await​

  • Rewrite a promise that resolves "Hello World" after 1 second using async/await.
💡 Show Answer
function helloPromise() {
return Promise.resolve("Hello World");
}
async function run() {
console.log(await helloPromise());
}
run();

2. getNumber async​

  • Create an async function getNumber() that waits 2 seconds, then returns a random number.
💡 Show Answer
async function getNumber() {
return new Promise((resolve) => {
setTimeout(() => {
resolve(Math.random());
}, 2000);
});
}

3. Sequential awaits​

  • Use await to call two promises sequentially, logging results one after the other.
💡 Show Answer
function getNumber() {
return new Promise((resolve) => {
setTimeout(() => {
resolve(Math.floor(Math.random() * 101));
}, 2000);
});
}

async function logNumbersSequentially() {
const num1 = await getNumber();
console.log("First number:", num1);

const num2 = await getNumber();
console.log("Second number:", num2);
}

logNumbersSequentially();

4. Parallel Promise.all​

  • Use Promise.all with await to run two promises in parallel and log both results.
💡 Show Answer
function getNumber() {
return new Promise((resolve) => {
setTimeout(() => {
resolve(Math.floor(Math.random() * 101));
}, 2000);
});
}

async function logNumbersInParallel() {
const [num1, num2] = await Promise.all([getNumber(), getNumber()]);
console.log("First number:", num1);
console.log("Second number:", num2);
}

logNumbersInParallel();

5. Handle rejection​

  • Write an async function with try...catch that safely handles a rejected promise.
💡 Show Answer
async function run() {
try {
await Promise.reject("err");
} catch (e) {
console.log("Caught", e);
}
}
run();
console.log("after run");

6. Process list with await​

  • Create a reusable delay(ms) function using await.
  • Use it to print numbers 1–5 with a 1-second pause each.
💡 Show Answer
function delay(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}

async function printNumbersWithDelay() {
for (let i = 1; i <= 5; i++) {
console.log(i);
await delay(1000);
}
}

printNumbersWithDelay();

7. Comparing Approaches​

  • Compare: write the same task once with .then() and once with async/await.
  • Which is clearer?

.then()

💡 Show Answer
function delay(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}

function printNumbersWithDelayThen(i = 1) {
if (i > 5) return;
console.log(i);
delay(1000).then(() => printNumbersWithDelayThen(i + 1));
}

printNumbersWithDelayThen();

async/await

💡 Show Answer
function delay(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}

async function printNumbersWithDelay(i = 1) {
if (i > 5) return;
console.log(i);
await delay(1000);
await printNumbersWithDelay(i + 1);
}

printNumbersWithDelay();

8. Accessing items using async/await​

  • Write an async function that fetches a list of items (simulate with a promise), then processes them one at a time using await.
💡 Show Answer
async function fetchItems() {
// Simulate fetching items with a promise
return new Promise((resolve) => {
setTimeout(() => {
resolve(["James Bond", "Shirley Temple", "Santa Claus"]);
}, 500);
});
}

async function processItem(item) {
// Simulate processing with a promise
return new Promise((resolve) => {
setTimeout(() => {
console.log(`Processed: ${item}`);
resolve();
}, 300);
});
}

async function fetchAndProcessItems() {
const items = await fetchItems();
for (const item of items) {
await processItem(item);
}
}

// Run the function
fetchAndProcessItems();

9. Random reject with try/catch​

  • Create a promise that randomly rejects half the time.
  • Use async/await and try...catch to handle success and failure.
💡 Show Answer
function rand() {
return new Promise((res, rej) =>
Math.random() > 0.5 ? res("ok") : rej("fail")
);
}
async function run() {
try {
console.log(await rand());
} catch (e) {
console.error(e);
}
}
run();

10. fetchUser → fetchOrders​

  • Build a mini flow:
    • fetchUser() → resolves with { name: "Alice" } after 1s.
    • fetchOrders(user) → resolves with ["Book", "Laptop"] after 1s.
    • Use async/await to log: "Alice ordered Book and Laptop".
💡 Show Answer
function fetchUser() {
return new Promise((resolve) => {
setTimeout(() => resolve({ name: "Alice" }), 1000);
});
}

function fetchOrders(user) {
return new Promise((resolve) => {
setTimeout(() => resolve(["Book", "Laptop"]), 1000);
});
}

async function main() {
const user = await fetchUser();
const orders = await fetchOrders(user);
console.log(`${user.name} ordered ${orders.join(" and ")}`);
}

main();