Autocodewizard Logo Asynchronous JavaScript - Autocodewizard Ebooks - JavaScript Essentials: From Fundamentals to Advanced Techniques

Chapter 11: Asynchronous JavaScript

Learn about asynchronous programming, including callbacks, promises, and async/await, to handle tasks that take time to complete.

In this chapter, we’ll explore asynchronous programming in JavaScript. Asynchronous code allows you to perform tasks like API calls and file loading without blocking other code execution, making your programs more responsive and efficient.

Understanding Asynchronous JavaScript

Asynchronous operations allow tasks to run in the background while the main code continues to execute. This is essential for tasks that take time, such as fetching data from a server. JavaScript provides several ways to handle asynchronous tasks, including callbacks, promises, and async/await.

Callbacks

A callback is a function passed as an argument to another function. It’s often used to execute code after an asynchronous task completes:

// Example: Callback function
function fetchData(callback) {
    setTimeout(() => {
        let data = "Sample data";
        callback(data);
    }, 2000);
}

fetchData((data) => {
    console.log("Data received:", data);
});

In this example, fetchData simulates an asynchronous task with setTimeout. After 2 seconds, it calls the callback function to process the data.

Promises

A promise is an object representing a future value. It can be in one of three states: pending, fulfilled, or rejected. Promises provide a cleaner way to handle asynchronous tasks compared to callbacks:

// Example: Promise
function fetchData() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            let success = true;
            if (success) {
                resolve("Data fetched successfully");
            } else {
                reject("Error fetching data");
            }
        }, 2000);
    });
}

fetchData()
    .then(data => console.log(data))
    .catch(error => console.error(error));

In this example, fetchData returns a promise. If the task succeeds, resolve is called, and the data is handled by then. If an error occurs, reject is called, and the error is handled by catch.

async/await

The async and await keywords provide a more readable way to work with promises. An async function automatically returns a promise, and await pauses the function execution until the promise resolves or rejects:

// Example: async/await
async function fetchData() {
    try {
        let response = await new Promise((resolve, reject) => {
            setTimeout(() => resolve("Data fetched successfully"), 2000);
        });
        console.log(response);
    } catch (error) {
        console.error("Error:", error);
    }
}

fetchData();

In this example, await pauses the function until the promise resolves, making the asynchronous code appear synchronous and easier to read.

Handling Multiple Promises with Promise.all

The Promise.all method allows you to run multiple promises in parallel and waits until all of them are resolved or one is rejected:

// Example: Promise.all
let promise1 = new Promise((resolve) => setTimeout(() => resolve("Promise 1"), 1000));
let promise2 = new Promise((resolve) => setTimeout(() => resolve("Promise 2"), 2000));

Promise.all([promise1, promise2])
    .then(results => console.log("Results:", results))
    .catch(error => console.error("Error:", error));

In this example, Promise.all waits for both promises to complete and then returns an array of results. If any promise rejects, Promise.all immediately rejects with that error.

Error Handling in Asynchronous Code

Error handling is crucial in asynchronous code to handle network failures or data retrieval issues. try...catch is commonly used with async/await to manage errors effectively:

// Example: Error handling with async/await
async function fetchData() {
    try {
        let response = await new Promise((resolve, reject) => {
            setTimeout(() => reject("Network error"), 2000);
        });
        console.log(response);
    } catch (error) {
        console.error("Error:", error);
    }
}

fetchData();

In this example, if an error occurs, it’s caught in the catch block, allowing for graceful error handling.

Summary and Next Steps

In this chapter, we covered asynchronous programming in JavaScript, including callbacks, promises, and async/await. Asynchronous code is essential for handling tasks that take time, such as API calls. In the next chapter, we’ll apply these concepts in a practical project to build an interactive web application using JavaScript.