Asynchronous Programming
Asynchronous programming allows a program to start a potentially long-running task (like fetching data from a server or reading a file) and continue executing other code while waiting for that task to finish, rather than blocking execution.
1. The Problem: Blocking vs Non-Blocking
- Synchronous (Blocking): Code executes line-by-line. If one line takes 5 seconds (e.g., a network request), the entire program freezes for 5 seconds.
- Asynchronous (Non-Blocking): The program initiates a task and moves on. When the task finishes, the program is notified.
2. Callbacks
The earliest way to handle async operations. You pass a function as an argument to another function, and that function is called ("called back") when the task is done.
// JavaScript Example
function fetchData(callback) {
setTimeout(() => {
callback("Data received");
}, 1000);
}
fetchData((data) => {
console.log(data);
});
Drawback: "Callback Hell" - deeply nested callbacks make code hard to read and maintain.
3. Promises
A Promise is an object representing the eventual completion (or failure) of an asynchronous operation. It allows you to chain operations linearly.
- States: Pending, Fulfilled (Resolved), Rejected.
// JavaScript Example
fetch("https://api.example.com/data")
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error(error));
4. Async / Await
Syntactic sugar built on top of Promises. It makes asynchronous code look and behave like synchronous code, making it much easier to read and debug.
async: Declares a function that returns a Promise.await: Pauses the execution of the function until the Promise is resolved.
// JavaScript Example
async function getData() {
try {
const response = await fetch("https://api.example.com/data");
const data = await response.json();
console.log(data);
} catch (error) {
console.error(error);
}
}