The Fetch API is the modern web standard for asynchronous networking. It is the engine that drives every single-page application (SPA) on the internet.
1The Global Fetch Function
Prior to 2015, making HTTP requests in JavaScript required a horrific, clunky API called XMLHttpRequest (XHR), or relying on external libraries like jQuery's $.ajax. The modern fetch() API solved this. It is globally available in the window object of all modern browsers, meaning you can open your browser console right now, type fetch('https://pokeapi.co/api/v2/pokemon/ditto'), and instantly execute a network request.
fetch("https://api.example.com/users");
2The Async Problem
JavaScript operates on a single thread. If it stops to wait 2 seconds for an API to respond, your entire website freezes (scrolling stops, animations halt). To fix this, fetch is Asynchronous. It immediately returns a 'Promise'—a ticket saying 'I owe you data later'. JavaScript continues running the rest of your UI code. When the network request finally finishes, the .then() block is pushed to the call stack and executed.
fetch("https://api.example.com/users")
.then(response => {
// This executes ONLY AFTER the server responds.
console.log("Data arrived!");
});
3The Double Resolution
Working with fetch requires a 'Double Resolution'. When the first Promise resolves, it does not give you the JSON data; it gives you a generic HTTP Response object (containing status codes and headers). To actually read the JSON body, you must call response.json(). However, response.json() is *also* asynchronous (because parsing a massive JSON string takes time). Thus, you must chain a second .then() to finally access the usable data.
fetch("https://api.com/users")
.then(response => response.json()) // Promise 1
.then(data => { // Promise 2: We finally have the JSON
console.log(data);
});
4Syntactic Sugar
Chaining multiple .then() blocks works, but it leads to nested, hard-to-read code. ES8 introduced async/await. This is 'syntactic sugar' over Promises. By placing the await keyword before fetch, the code *appears* synchronous and linear, making it much easier to read. Under the hood, it is still completely non-blocking and asynchronous. You must wrap any await calls inside a function marked with the async keyword.
async function getUsers() {
const response = await fetch("/users");
const data = await response.json();
console.log(data);
}
