Up to this point, our code has been synchronous—it runs sequentially, one line after the other. However, tasks like fetching data from a server, setting a timer, or accessing a user's location can take time. If JavaScript waited for these tasks to finish, the browser would freeze. Asynchronous JavaScript is the solution, allowing code to run in the background without blocking the main program execution.
The JavaScript Event Loop
JavaScript is single-threaded, but the browser provides Web APIs (like setTimeout
, fetch
, DOM) that can handle time-consuming tasks outside the main thread. The Event Loop is the mechanism that ensures when these tasks complete, their associated callback functions are pushed back onto the main thread for execution without blocking the UI.
Timers: setTimeout
and setInterval
These are the simplest forms of asynchronous operations.
1. setTimeout()
: Delayed Execution
Runs a function once after a specified delay (in milliseconds).
JavaScript
console.log('Start');
setTimeout(() => {
console.log('This runs after 3 seconds (asynchronously)');
}, 3000);
console.log('End (This runs immediately)');
// Output order: Start, End, ... 3 seconds later: This runs...
To cancel a timeout, save its ID and use clearTimeout()
:
JavaScript
const timerID = setTimeout(() => console.log('This will be canceled'), 5000);
clearTimeout(timerID); // The function never runs
2. setInterval()
: Repetitive Execution
Runs a function repeatedly, starting after the specified interval.
JavaScript
let count = 0;
const intervalID = setInterval(() => {
count++;
console.log(`Count: ${count}`);
if (count >= 5) {
clearInterval(intervalID); // Stop the interval
}
}, 1000);
Introduction to Promises
Before Promises, asynchronous code relied heavily on callbacks, often leading to messy, hard-to-read code (known as "Callback Hell"). Promises are a modern object-based solution for handling asynchronous results.
A Promise represents a value that is not necessarily known when the promise is created. It is a container for a future value.
A Promise is always in one of three states:
Pending: Initial state, neither fulfilled nor rejected.
Fulfilled (Resolved): The operation completed successfully, and the promise has a resulting value.
Rejected: The operation failed, and the promise has a reason for the failure (an error object).
Consuming Promises
We consume the eventual result of a Promise using the .then()
and .catch()
methods.
.then(onFulfilled, onRejected)
: Registers callbacks to be called when the Promise is fulfilled or rejected.
.catch(onRejected)
: A shorthand for .then(null, onRejected)
, typically used for error handling at the end of a chain.
.finally()
: Runs regardless of success or failure (good for cleanup).
JavaScript
// Imagine a function that returns a Promise
const myPromise = new Promise((resolve, reject) => {
// Simulate a successful operation
setTimeout(() => resolve('Data loaded successfully'), 2000);
});
myPromise
.then(result => {
console.log('Success:', result); // Output: Data loaded successfully
return 'Processed: ' + result; // Pass result to the next .then()
})
.then(processedData => {
console.log(processedData);
})
.catch(error => {
console.error('An error occurred:', error);
})
.finally(() => {
console.log('Promise finished.');
});
Fetching Data with the fetch
API
The fetch
API is the standard way to make network requests in modern JavaScript, and it is Promise-based.
JavaScript
// Fetch data from an external API (Application Programming Interface)
fetch('https://api.example.com/data')
.then(response => {
// The first .then() checks if the request was successful and parses the response body (e.g., as JSON)
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.json(); // Returns a new Promise with the parsed JSON data
})
.then(data => {
// The second .then() receives the final JavaScript object
console.log('Received data:', data);
// Display data in the DOM here
})
.catch(error => {
// Catches any error during the fetch or the parsing
console.error('There was a problem with the fetch operation:', error);
});