Facebook Pixel

Best Practices for Callbacks in JavaScript

Callbacks can be messy. Here are the best practices to write clean, maintainable callback code.

Best Practices for Callbacks in JavaScript

Callbacks can lead to messy code if not handled well. Here are the best practices to write clean, maintainable callback-based code.

1. Prefer Promises or Async/Await

For new code, use promises or async/wait instead of plain callbacks. They are more readable, have better error handling, and avoid callback hell.

2. Use Named Functions Instead of Anonymous Ones

// bad: anonymous, cannot remove, hard stack trace button.addEventListener("click", () => { ... }); // good: named, removable, clear stack trace function handleClick(event) { ... } button.addEventListener("click", handleClick); // later: button.removeEventListener("click", handleClick); `` ### 3. Handle Errors (Node.js Convention) ```js fs.readFile("file.txt", (err, data) => { if (err) { console.error(err); return; } console.log(data); }); `` Always check for the error first. Never ignore it. ### 4. Keep Callback Nesting Shallow ```js // bad: deep nesting fetchUser(id, (err, user) => { fetchPosts(user, (err, posts) => { fetchComments(posts[0], (err, comments) => { render(user, posts, comments); }); }); }); // better: named functions fetchUser(id, handleUser); function handleUser(err, user) { if (err) return console.error(err); fetchPosts(user, (err, posts) => handlePosts(err, user, posts)); } function handlePosts(err, user, posts) { if (err) return console.error(err); fetchComments(posts[0], (err, comments) => handleComments(err, user, posts, comments)); } function handleComments(err, user, posts, comments) { if (err) return console.error(err); render(user, posts, comments); } `` Or better: convert to promises. ### 5. Remove Event Listeners When Done ```js const handler = () => { ... }; button.addEventListener("click", handler); // when the element is removed or the component unmounts: button.removeEventListener("click", handler); `` ### 6. Use `once` for One-Time Listeners ```js button.addEventListener("click", handler, { once: true }); // automatically removed after the first call `` ### 7. Use Event Delegation for Dynamic Children ```js document.querySelector("#list").addEventListener("click", (event) => { if (event.target.matches("li")) { ... } }); `` One listener handles all children, including dynamically added ones. ### 8. Avoid Closures Over Large Objects ```js // bad: keeps big alive const big = new Array(1_000_000); setTimeout(() => console.log(big.length), 0); // good: extract what you need const len = big.length; setTimeout(() => console.log(len), 0); `` ### 9. Use `Promise.all` for Parallel Callbacks ```js Promise.all([fetchA(), fetchB(), fetchC()]).then(([a, b, c]) => { ... }); `` ### The Takeaway Prefer promises/async-await for new code. Use named functions for removable listeners. Handle errors (Node.js convention). Keep nesting shallow. Remove listeners when done. Use `once` for one-time listeners. Use event delegation for dynamic children. Avoid closing over large objects. Use `Promise.all` for parallel operations.

Prefer promises or async/await for new code. Use named functions for removable listeners. Handle errors (Node.js convention). Keep nesting shallow. Remove listeners when done. Use event delegation for dynamic children. Avoid closing over large objects.

Use promises (chain .then calls) or async/await (flatten to synchronous-looking code). If you must use callbacks, extract them into named functions to keep nesting shallow. Or use a library like async.js for control flow.

Named functions. They can be removed with removeEventListener (you need the same reference), produce better stack traces, and are easier to debug. Anonymous functions cannot be removed and show as <anonymous> in traces.

The Node.js convention: the first argument is an error. Always check if (err) first and handle it. Never ignore the error argument. In promises, use .catch. In async/await, use try/catch.

addEventListener(type, handler, { once: true }) automatically removes the listener after the first call. This is useful for one-time events and avoids the need to manually call removeEventListener.

Ready to master React completely?

Want to upskill yourself, crack your next interview, and get your dream job? Join our comprehensive course to dive deeper with high-quality video tutorials, solve interview questions, and a premium community.

Please Login.
Please Login.
Please Login.
Please Login.
Please Login.
Please Login.
Please Login.
Please Login.
Please Login.
Please Login.
Please Login.
Please Login.