Async JavaScript
Asynchronous code allows operations (API calls, file reads, timers) to run without blocking the rest of the program. JavaScript is single-threaded, so async patterns are critical.
Without async, a single network request would freeze your entire UI for seconds. Async lets your app stay responsive while waiting for slow operations.
Every API call, database query, file operation, timer, or any operation that takes time. In React + Next.js this is used constantly.
Use async/await syntax. Mark a function with async, then await any Promise-returning call. Wrap in try/catch for error handling.
📚Key Terms
Promise— An object representing a future value. Has three states: pending, fulfilled, rejected
async function— A function that automatically returns a Promise. Allows use of await inside it
await— Pauses execution inside an async function until a Promise resolves. Like 'wait for this to finish'
Event Loop— JavaScript's mechanism for handling async operations. The call stack, web APIs, and callback queue work together
Code Examples
#1Promises → async/await (modern way)
// OLD WAY: Promise chains (callback hell)fetch("https://jsonplaceholder.typicode.com/users/1").then(response => response.json()).then(user => {console.log(user);return fetch(`/api/posts?userId=${user.id}`);}).then(response => response.json()).then(posts => console.log(posts)).catch(error => console.error(error)); // one catch for all// ✅ MODERN WAY: async/await — reads like synchronous codeasync function fetchUserAndPosts(userId) {try {// await pauses here until response comes backconst userResponse = await fetch(`https://jsonplaceholder.typicode.com/users/${userId}`);if (!userResponse.ok) {throw new Error(`HTTP error! status: ${userResponse.status}`);}const user = await userResponse.json(); // await again for parsingconsole.log("User:", user);const postsResponse = await fetch(`https://jsonplaceholder.typicode.com/posts?userId=${userId}`);const posts = await postsResponse.json();console.log("Posts:", posts);return { user, posts };} catch (error) {console.error("Something went wrong:", error.message);throw error; // re-throw so caller can handle it}}// Call async functionfetchUserAndPosts(1);
#2Parallel async with Promise.all
// Running multiple async operations in PARALLELasync function fetchDashboardData(userId) {try {// ❌ Sequential — slow! Each waits for previous// const user = await getUser(userId);// const posts = await getPosts(userId);// const comments = await getComments(userId);// ✅ Parallel — all 3 start at the same time!const [user, posts, comments] = await Promise.all([fetch(`https://jsonplaceholder.typicode.com/users/${userId}`).then(r => r.json()),fetch(`https://jsonplaceholder.typicode.com/posts?userId=${userId}`).then(r => r.json()),fetch(`https://jsonplaceholder.typicode.com/comments?postId=1`).then(r => r.json()),]);// All three are done by this linereturn { user, posts, comments };} catch (error) {// Promise.all rejects if ANY promise failsconsole.error(error);}}// Promise.allSettled — doesn't fail if one rejectsconst results = await Promise.allSettled([fetch('/api/users').then(r => r.json()),fetch('/api/broken-endpoint').then(r => r.json()),]);// results[0] = { status: 'fulfilled', value: [...] }// results[1] = { status: 'rejected', reason: Error }
Practice This Concept
Open the playground, paste any example, and experiment. The best way to learn is by breaking things.
Open in Playground