← JavaScript EnglishChapter 09 of 13

Asynchronous JavaScript

## Learning Objectives - Understand the event loop and asynchronous programming - Master callbacks, promises, and async/await - Handle errors in asynchronous code ## Synchronous vs Asynchronous ### Synchronous Code ```javascript console.log("First"); console.log("Second"); console.log("Third"); // Output: First, Second, Third (in order) ``` ### Asynchronous Code ```javascript console.log("First"); setTimeout(() => console.log("Second"), 1000); console.log("Third"); // Output: First, Third, Second (after 1 second) ``` ## The Event Loop JavaScript is single-threaded but uses an event loop to handle asynchronous operations. ```javascript console.log("Start"); setTimeout(() => console.log("Timeout"), 0); Promise.resolve().then(() => console.log("Promise")); console.log("End"); // Output: Start, End, Promise, Timeout ``` Order of execution: 1. Synchronous code first 2. Microtasks (Promises) next 3. Macrotasks (setTimeout, setInterval) last ## Callbacks ```javascript function fetchData(callback) { setTimeout(() => { const data = { name: "Alice", age: 30 }; callback(null, data); }, 1000); } function handleError(error) { console.error("Error:", error); } fetchData((error, data) => { if (error) { handleError(error); return; } console.log("Data:", data); }); ``` ### Callback Hell ```javascript fetchData((err, data) => { if (err) return handleError(err); processData(data, (err, result) => { if (err) return handleError(err); saveResult(result, (err) => { if (err) return handleError(err); sendNotification((err) => { if (err) return handleError(err); console.log("Done!"); }); }); }); }); ``` ## Promises ```javascript const promise = new Promise((resolve, reject) => { const success = true; if (success) { resolve({ data: "Success!" }); } else { reject(new Error("Failed!")); } }); promise .then(result => console.log(result)) .catch(error => console.error(error)) .finally(() => console.log("Completed")); ``` ### Promise States - **Pending**: Initial state - **Fulfilled**: Operation completed successfully - **Rejected**: Operation failed ### Creating Promises ```javascript // From callback function fetchData() { return new Promise((resolve, reject) => { setTimeout(() => resolve("Data"), 1000); }); } // From value const resolvedPromise = Promise.resolve(42); const rejectedPromise = Promise.reject(new Error("Oops!")); // From existing promise const promiseToPromise = Promise.resolve(fetchData()); ``` ### Promise Chain ```javascript fetchUser(userId) .then(user => fetchPosts(user.id)) .then(posts => filterPublicPosts(posts)) .then(publicPosts => sortByDate(publicPosts)) .then(sorted => displayPosts(sorted)) .catch(error => displayError(error)); ``` ### Promise.all() ```javascript const promises = [ fetchUser(1), fetchPosts(1), fetchComments(1) ]; Promise.all(promises) .then(([user, posts, comments]) => { console.log(user, posts, comments); }) .catch(error => console.error("One failed:", error)); ``` ### Promise.allSettled() ```javascript const promises = [ fetchUser(1), fetchPosts(1), fetchSomeFailingOperation() ]; Promise.allSettled(promises) .then(results => { results.forEach((result, index) => { if (result.status === "fulfilled") { console.log(`${index}: ${result.value}`); } else { console.log(`${index}: ${result.reason}`); } }); }); ``` ### Promise.race() ```javascript const slow = new Promise(resolve => setTimeout(() => resolve("slow"), 1000)); const fast = new Promise(resolve => setTimeout(() => resolve("fast"), 500)); Promise.race([slow, fast]) .then(result => console.log(result)); // "fast" (whichever resolves first) ``` ### Promise.any() ```javascript const promises = [ fetchWithTimeout("/api/1", { timeout: 1000 }), fetchWithTimeout("/api/2", { timeout: 2000 }), fetchWithTimeout("/api/3", { timeout: 3000 }) ]; Promise.any(promises) .then(result => console.log(result)) .catch(errors => console.log("All failed:", errors)); ``` ## async/await ### Basic Syntax ```javascript async function fetchData() { const response = await fetch("/api/data"); const data = await response.json(); return data; } fetchData().then(console.log); ``` ### async Function Returns ```javascript async function getValue() { return 42; // Returns Promise.resolve(42) } getValue().then(console.log); // 42 async function getError() { throw new Error("Oops!"); } // Returns Promise.reject(Error("Oops!")) ``` ### await Expression ```javascript // await pauses execution until promise settles const result = await somePromise; // Equivalent to somePromise.then(result => { // use result here }); ``` ### Error Handling with try/catch ```javascript async function fetchData() { try { const response = await fetch("/api/data"); const data = await response.json(); return data; } catch (error) { console.error("Fetch failed:", error); throw error; } } // Or catch at call site try { const data = await fetchData(); } catch (error) { console.error("Error:", error); } ``` ### Parallel Execution ```javascript async function fetchAll() { // Sequential (slower) const user = await fetchUser(); const posts = await fetchPosts(); // Parallel (faster) const [user, posts] = await Promise.all([ fetchUser(), fetchPosts() ]); } ``` ### Sequential vs Parallel ```javascript // Sequential - takes 3 seconds (1s + 1s + 1s) async function sequential() { const a = await task1(); // 1s const b = await task2(); // 1s const c = await task3(); // 1s return [a, b, c]; } // Parallel - takes 1 second (all run together) async function parallel() { const [a, b, c] = await Promise.all([ task1(), // 1s task2(), // 1s task3() // 1s ]); return [a, b, c]; } ``` ### Common Patterns ```javascript // Process array with async function const urls = ["/api/1", "/api/2", "/api/3"]; async function fetchAll() { return Promise.all(urls.map(url => fetch(url))); } // Sequential processing when order matters async function fetchSequential() { const results = []; for (const url of urls) { const result = await fetch(url); results.push(result); } return results; } ``` ## Converting Callbacks to Promises ```javascript // Callback style function fetchData(callback) { setTimeout(() => callback(null, "data"), 1000); } // To Promise function fetchData() { return new Promise((resolve, reject) => { fetchData((error, result) => { if (error) reject(error); else resolve(result); }); }); } // Using util.promisify (Node.js) const { promisify } = require("util"); const fs = require("fs"); const readFile = promisify(fs.readFile); ``` ## Summary - JavaScript is single-threaded but handles async via event loop - Callbacks are the original async pattern but lead to callback hell - Promises provide better error handling and chaining - `async/await` makes asynchronous code look synchronous - Use `Promise.all()` for parallel execution when possible - Always handle errors in async code with `.catch()` or try/catch

Comments

Comments powered by Giscus

To enable comments, add your Giscus embed code here.

Learn more about Giscus →