JavaScript runs in a single thread, which means long-running operations — like network requests or file reads — would freeze the browser if they ran synchronously. Async/await is the modern solution.
The problem: blocking code
// This would freeze the browser if fetch took a second:
const data = fetch('https://api.example.com/data'); // WRONGPromises
A Promise is an object that represents a value that may not be available yet. It's either pending, fulfilled, or rejected.
const promise = fetch('https://api.example.com/data');
promise
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error(error));This works but chained .then() calls get messy with complex logic.
Async/Await
async and await make asynchronous code look synchronous, which is much easier to read:
async function getData() {
const response = await fetch('https://api.example.com/data');
const data = await response.json();
console.log(data);
}
getData();The await keyword pauses execution inside the async function until the Promise resolves. The rest of the program continues running — this is not blocking.
Error handling
Wrap await calls in try/catch:
async function getData() {
try {
const response = await fetch('https://api.example.com/data');
const data = await response.json();
return data;
} catch (error) {
console.error('Request failed:', error);
}
}Running multiple async operations
To run multiple operations in parallel:
const [users, posts] = await Promise.all([
fetch('/api/users').then(r => r.json()),
fetch('/api/posts').then(r => r.json()),
]);Promise.all waits for both to complete. If either fails, the whole thing rejects.
When to use async/await
Any time you're working with:
- •fetch() — network requests
- •setTimeout() wrapped in a promise
- •File system operations in Node.js
- •Database queries in server code
async/await doesn't change how JavaScript works — it's syntactic sugar over Promises. Understanding Promises is still useful for reading older code and handling complex scenarios.