Asynchronous JavaScript
JavaScript is fundamentally a single-threaded, synchronous language. This means it has exactly one Call Stack and one Memory Heap. It can only execute one line of code at a time.
But what happens when you need to fetch 5GB of data from a remote server? If JS waits sequentially, the entire browser would freeze and crash.
The Event Loop and Callbacks
To bypass freezing, JavaScript relies on the browser's internal C++ Web APIs to handle slow tasks in the background. Once the background task finishes, it relies on a Callback to jump back onto the main execution thread.
console.log("1. Start script");
// setTimeout is an Asynchronous Web API
setTimeout(() => {
console.log("2. Wait 2 seconds (Timeout resolved!)");
}, 2000);
console.log("3. End script");
// Console Execution output:
// "1. Start script"
// "3. End script"
// ... (2 seconds pass) ...
// "2. Wait 2 seconds"
Because setTimeout takes 2 seconds, JS delegates it to the browser's background pool and immediately moves on to execute console.log("3. End script"). This prevents code-blocking!
The Callback Hell Problem
Early JavaScript relied entirely on nested callbacks to handle synchronous logic. Tracking nested callbacks generated incredibly unreadable code.
getData(function(response) {
parseData(response, function(parsed) {
saveData(parsed, function(success) {
console.log("Data saved successfully!");
});
});
});
This rapidly scaling pyramid structure is affectionately known to developers as "Callback Hell".
It was solved in ES6 via Promises.