Asynchronous
JavaScript is often used for tasks that take time, like fetching data from a server or waiting for user actions. To keep your application responsive, JavaScript handles these tasks asynchronously. This means it doesn't wait for one task to finish before starting the next.
What is Asynchronous JavaScript?
In synchronous programming, tasks are executed one after the other. In asynchronous programming, tasks that take time run in the background, allowing other tasks to continue.
Example: Synchronous JavaScript
console.log("Task 1"); console.log("Task 2"); console.log("Task 3"); // Outputs: // Task 1 // Task 2 // Task 3
Example: Asynchronous JavaScript
console.log("Task 1"); setTimeout(() => { console.log("Task 2"); }, 2000); // Waits 2 seconds console.log("Task 3"); // Outputs: // Task 1 // Task 3 // Task 2 (after 2 seconds)
Common Asynchronous Operations
- Fetching data from APIs
- Reading or writing files
- Timers (setTimeout and setInterval)
- User interactions like clicks or inputs
Callbacks
A callback is a function passed as an argument to another function. It's executed after the first function finishes its task.
Example: Using Callbacks
function greetUser(name, callback) { console.log("Hello, " + name); callback(); } function sayGoodbye() { console.log("Goodbye!"); } greetUser("Alice", sayGoodbye); // Outputs: // Hello, Alice // Goodbye!
Drawback of Callbacks: Callback Hell
When multiple callbacks are nested, the code can become hard to read and maintain.
setTimeout(() => { console.log("Step 1"); setTimeout(() => { console.log("Step 2"); setTimeout(() => { console.log("Step 3"); }, 1000); }, 1000); }, 1000);
Promises
A Promise represents a task that will complete in the future. It can either:
- Resolve (successful result)
- Reject (error occurred)
Syntax of a Promise
let promise = new Promise((resolve, reject) => { // Asynchronous task if (/* success */) { resolve("Task completed"); } else { reject("Task failed"); } });
Using Promises
let fetchData = new Promise((resolve, reject) => { let success = true; // Simulate success or failure if (success) { resolve("Data fetched successfully"); } else { reject("Error fetching data"); } }); fetchData .then((message) => { console.log(message); // If resolved }) .catch((error) => { console.log(error); // If rejected });
Chaining Promises
fetchData .then((message) => { console.log(message); return "Next step"; }) .then((nextMessage) => { console.log(nextMessage); }) .catch((error) => { console.log(error); });
Async and Await
The async and await keywords make working with asynchronous code easier and more readable. They allow you to write asynchronous code that looks like it's synchronous.
Syntax
- Use async to declare a function that works with await.
- Use await to wait for a promise to resolve before moving to the next line.
Example:
async function fetchData() { try { let data = await new Promise((resolve, reject) => { setTimeout(() => resolve("Data fetched!"), 2000); }); console.log(data); } catch (error) { console.log("Error:", error); } } fetchData(); // Outputs (after 2 seconds): Data fetched!
Combining Async/Await and Promises
You can use async/await for cleaner code, especially when working with multiple asynchronous tasks.
Example: Fetching Data
function getData() { return new Promise((resolve, reject) => { setTimeout(() => resolve("Data fetched"), 2000); }); } async function processData() { try { let result = await getData(); console.log(result); console.log("Processing complete"); } catch (error) { console.log("Error:", error); } } processData(); // Outputs: // (After 2 seconds) Data fetched // Processing complete
Real-World Example: Fetching an API
Using fetch and Promises
fetch("https://jsonplaceholder.typicode.com/posts/1") .then((response) => response.json()) .then((data) => { console.log("Post:", data); }) .catch((error) => { console.log("Error:", error); });
Using Async/Await
async function getPost() { try { let response = await fetch("https://jsonplaceholder.typicode.com/posts/1"); let data = await response.json(); console.log("Post:", data); } catch (error) { console.log("Error:", error); } } getPost();
Error Handling in Asynchronous Code
1. Using .catch() for Promises
fetch("invalid-url") .then((response) => response.json()) .catch((error) => console.log("Fetch Error:", error));
2. Using try...catch with Async/Await
async function fetchData() { try { let response = await fetch("invalid-url"); let data = await response.json(); } catch (error) { console.log("Error:", error); } } fetchData();