Loading...
Loading...
Functions passed as arguments. The foundation of async JavaScript.
Functions are reusable blocks of logic — the primary unit of organization in JavaScript. Parameters pass data in, return values send results out, and closures let functions remember their environment long after they were created.
Learn Callbacks in JavaScript — covering What Are Callbacks?, Synchronous Callbacks, Async Callbacks, Error-First Pattern with interactive visual examples and step-by-step explanations.
Functions are the building blocks of every JavaScript application. Instead of repeating the same logic, you wrap it in a named block, pass in data, and get a result back. Arrow functions, default parameters, and rest/spread syntax make modern function code concise and readable.
This module also introduces closures — one of JavaScript's most powerful and misunderstood features. Interactive diagrams show how functions retain access to variables from their outer scope, which is essential for callbacks, event handlers, and module patterns.
Understanding callbacks is essential for every JavaScript developer. It shows up in frontend UI code, backend APIs, and framework internals — and getting it wrong leads to bugs that are hard to trace. This chapter builds intuition with visuals so the behavior sticks.
A function passed to another function to be called later.
function greet(name, callback) { const message = "Hello, " + name; callback(message); } function log(text) { console.log(text); } greet("Alice", log); // "Hello, Alice"
'log' is passed as a callback. greet() calls it when the message is ready.
Executed immediately within the calling function.
// Array methods use sync callbacks: const nums = [1, 2, 3, 4, 5]; nums.forEach((n) => console.log(n)); // logs each nums.map((n) => n * 2); // [2, 4, 6, 8, 10] nums.filter((n) => n > 3); // [4, 5] nums.find((n) => n === 3); // 3 nums.sort((a, b) => a - b); // sorted
// .filter() with a callback:
Executed later, when an operation completes.
// setTimeout — callback runs after delay: setTimeout(() => { console.log("2 seconds passed!"); }, 2000); // Event listener — callback runs on event: button.addEventListener("click", () => { console.log("Clicked!"); }); // File reading (Node.js): fs.readFile("data.txt", (err, data) => { console.log(data); });
// setTimeout demo:
Node.js convention: first parameter is always the error.
// Error-first callback pattern: fs.readFile("data.txt", (err, data) => { if (err) { console.error("Failed:", err.message); return; } console.log("Got:", data); }); // Creating your own: function fetchUser(id, callback) { if (!id) { callback(new Error("ID required"), null); return; } // ... fetch logic callback(null, userData); // success: err=null }
Convention rules:
err (or null on success)Nested callbacks make code hard to read and maintain.
✗ Callback hell
getUser(id, (err, user) => {
getPosts(user.id, (err, posts) => {
getComments(posts[0], (err, comments) => {
render(comments, (err) => {
// 😵 deeply nested
});
});
});
});✓ Modern alternative (Promises)
// Flat and readable: const user = await getUser(id); const posts = await getPosts(user.id); const comments = await getComments(posts[0]); await render(comments);
💡 Callbacks are still fundamental to understand, but modern JavaScript uses Promises and async/await for cleaner async code.
Common questions about callbacks.