Some things in JavaScript don't finish instantly — fetching data from a server, reading a file, waiting on a timer. JavaScript handles this kind of "finish later" work with something called a Promise, and honestly, this is one of the topics that takes a while to feel comfortable with, so don't worry if it feels a bit abstract at first.
What a Promise represents
A Promise is an object representing a value that doesn't exist yet, but will eventually — either successfully, or with an error. Think of it as a receipt: you don't have your food yet, but you've something that guarantees you'll get either the food or an explanation of what went wrong.
Using .then()
fetch("https://api.example.com/users")
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.log("Something went wrong:", error));Each .then() runs once the previous step finishes successfully, and .catch() runs if anything along the chain fails. This works, and you'll see it in plenty of real code, but chaining a lot of .then() calls together can get hard to read once there are more than two or three steps.
async/await: the same thing, easier to read
async function getUsers() {
try {
const response = await fetch("https://api.example.com/users");
const data = await response.json();
console.log(data);
} catch (error) {
console.log("Something went wrong:", error);
}
}This does exactly the same thing as the .then() version above, but it reads almost like ordinary, top-to-bottom code, which is a genuinely big deal once your logic gets more complicated than two chained steps. The await keyword essentially pauses the function right there until the Promise finishes, without freezing the rest of the page while it waits.
Two important rules
- await only works inside a function marked async — you can't just sprinkle it anywhere.
- Wrap awaited code in try/catch to handle failures, the same way you would with any other error-prone code.
Why does any of this matter?
JavaScript runs on a single thread, meaning it can only do one thing at a time. If fetching data from a server just froze everything until it finished, every website would feel completely unresponsive while anything loaded. Promises, and async/await on top of them, let JavaScript start a slow operation, immediately move on to other work, and come back to handle the result the moment it actually arrives.
Don't overthink it early on
If this still feels a little fuzzy, that's completely normal. Write a few small examples that fetch something and log it to the console, break them on purpose, and see what the error messages look like. This concept genuinely clicks with practice more than with reading about it.