Warming up the neural circuits...
So far, all your code has been synchronous — each line runs, finishes, and then the next line starts. But the web is inherently asynchronous: fetching data from a server, waiting for a timer, reading a file — these operations take time and shouldn't block the of your code.
// Synchronous (blocks until done)
console.log("Start");
const result = heavyComputation(); // Takes 5 seconds — everything waits!
console.log("End"); // Runs after 5 seconds
// Asynchronous (doesn't block)
console.log("Start");
fetch("https://api.example.com/data") // Starts, doesn't wait
.then(data => console.log("Data received"));
console.log("End"); // Runs immediately, data arrives laterOutput ():
Start
End
Data received ← arrives when server responds// Imagine you're at a restaurant:
// Synchronous (blocking)
// You order food. The waiter stands at your table until the food is ready.
// No other customers can be served during this time.
// Asynchronous (non-blocking)
// You order food. The waiter gives the order to the kitchen and serves other tables.
// When your food is ready, the waiter brings it to you.On the web: If fetching data from an blocked the browser, the page would freeze. No scrolling, no clicking, no typing. Async prevents this.
A callback is a function passed to another function to run later:
function fetchData(callback) {
setTimeout(() => {
const data = { id: 1, name: "Alice" };
callback(data);
}, 1000);
}
console.log("
Problem: Callback Hell
getUser(id, (user) => {
getPosts(user.id, (posts) => {
getComments(posts[0].id, (comments) => {
// Nesting keeps growing — code becomes unreadable!
This is why Promises were created.
A represents a value that may be available now, later, or never.
// Creating a Promise
const myPromise = new Promise((resolve, reject) => {
const success = true;
setTimeout(() => {
if (success) {
resolve("✅ Operation succeeded!
Pending ──→ Fulfilled (resolve called)
Pending ──→ Rejected (reject called)
Pending ──→ Settled (either fulfilled or rejected)Once settled, a Promise can't change . A Promise is not lazy — it starts executing as soon as it's created.
fetch() is the modern way to make HTTP requests in . It returns a Promise.
// GET request
fetch("https://api.github.com/users/octocat")
.then(response => {
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
// GET — Fetch data
fetch("https://api.example.com/users");
// POST — Create data
fetch("https://api.example.com/users", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON
async/await is syntactic sugar over Promises — it makes async code read like synchronous code.
// Promise-based
function getData() {
fetch("https://api.example.com/data")
.then(res => res.json())
.then(data => console.log(data))
.catch(err
Rules:
await can only be used inside an async functionasync functions always return a Promisetry/catch for error handling with async/awaitasync function getSequential() {
const user = await fetch("/api/user").then(r => r.json());
const posts = await fetch(`/api/user/${user.id}/posts`).
async function getParallel() {
const [user, products, notifications] = await Promise.all([
fetch("/api/user").then(r => r.json()),
fetch("/api/products
async function getFastest() {
const result = await Promise.race([
fetch("https://api1.example.com/data"),
fetch("https://api2.example.com/data"),
]);
return result.json();
}const results = await Promise.allSettled([
fetch("/api/endpoint1"),
fetch("/api/endpoint2"),
fetch("/api/endpoint3"),
]);
results.forEach(r => {
if
async function getGithubProfile(username) {
try {
// Fetch user and repos in parallel
const [userResponse, reposResponse] = await Promise.all([
fetch(`https://api.github.com/users/${username}`),
fetch(`https://api.github.com/users/${
Mistake 1: Forgetting to await
// ❌ Wrong (returns a Promise, not the data)
function fetchUser(id) {
const response = fetch(`/api/users/${id}`); // Promise!
return response.json(); // Error: response.json doesn't exist on Promise
}
//
Mistake 2: Not checking response.ok
// ❌ Wrong (404 response doesn't throw — it's still a valid HTTP response)
const response = await fetch("/api/nonexistent");
const data = await response.json(); // Might be error HTML, not JSON
// ✅ Right
const response = await fetch("
Mistake 3: Sequential when you could parallel
// ❌ Slow (sequential)
const user = await fetchUser();
const products = await fetchProducts();
const posts = await fetchPosts();
// ✅ Fast (parallel)
const [user, products, posts] =
Mistake 4: Not catching errors
// ❌ Unhandled rejection (crashes)
async function load() {
const data = await fetch("/api/data");
}
// ✅ Handle errors
async function load() {
try {
const data = await fetch("/api/data