The Difference Between JavaScript Callbacks and Promises

Hero image for The Difference Between JavaScript Callbacks and Promises. Image by Adam Wilson.
Hero image for 'The Difference Between JavaScript Callbacks and Promises.' Image by Adam Wilson.

In JavaScript, handling asynchronous operations effectively is very important when we're building modern applications. Two primary approaches for managing asynchronous behaviour are callbacks and promises. Whilst both aim to solve similar problems, they differ in structure, usability, and maintainability.

In this article, I intend to explore the key differences between callbacks and promises, their advantages, and when to use each. By the end, you should hopefully have a clear understanding of how to work with both techniques and choose the right tool for the task.


What are Callbacks?

A callback is a function passed as an argument to another function, which is then executed at a later time. Callbacks have been a foundational concept in JavaScript for handling asynchronous tasks like reading files, making HTTP requests, or responding to user interactions.

Example of a Callback

function fetchData(url: string, callback: (data: string) => void): void {  setTimeout(() => {    const data = `Data from ${url}`;    callback(data);  }, 1000);}fetchData("https://api.example.com", (data) => {  console.log(data);  // Output: Data from https://api.example.com});

In this example:

  • The fetchData function simulates an asynchronous operation using setTimeout.
  • The callback function is executed when the data is ready, allowing us to process it.

Downsides of Callbacks

  • Callback Hell

    : Nesting multiple callbacks can result in deeply nested and hardtoread code.
  • Error Handling

    : Managing errors across multiple callbacks is cumbersome and often inconsistent.

What are Promises?

A promise is a JavaScript object that represents the eventual completion (or failure) of an asynchronous operation and its resulting value. Promises offer a more structured and readable way to handle asynchronous tasks.

Example of a Promise

function fetchData(url: string): Promise<string> {  return new Promise((resolve, reject) => {    setTimeout(() => {      const data = `Data from ${url}`;      resolve(data);    }, 1000);  });}fetchData("https://api.example.com")  .then((data) => {    console.log(data);  // Output: Data from https://api.example.com  })  .catch((error) => {    console.error("Error:", error);  });

In this example:

  • The fetchData function returns a Promise.
  • The then method handles the resolved value, whilst catch handles errors, making the code more structured.

Advantages of Promises

  • Chaining

    : Promises allow chaining multiple asynchronous operations using .then().
  • Error Propagation

    : Errors can be caught at any point in the chain with .catch(), providing a cleaner errorhandling mechanism.

Key Differences Between Callbacks and Promises

FeatureCallbacksPromises
SyntaxFunction passed as an argumentObject with .then() and .catch() methods
ReadabilityCan lead to nested code (callback hell)Cleaner and easier to follow with chaining
Error HandlingMust handle errors manually at each stepCentralised error handling with .catch()
FlexibilityLimited to specific callback patternsSupports chaining and more complex flows

When to Use Callbacks or Promises

Use Callbacks When:

  • You're working with older APIs or libraries that don't support promises.
  • The task is simple and doesn't require chaining or complex error handling.

Use Promises When:

  • You're writing modern, maintainable code.
  • The task involves multiple asynchronous steps that benefit from chaining.
  • You want better error handling and improved readability.

Moving Forward: Async/await

Whilst callbacks and promises are both valid approaches, modern JavaScript developers often use async/await, a syntactic sugar built on top of promises to simplify asynchronous code further. For example:

async function fetchData(url: string): Promise<string> {  return new Promise((resolve, reject) => {    setTimeout(() => {      const data = `Data from ${url}`;      resolve(data);    }, 1000);  });}async function getData(): Promise<void> {  try {    const data = await fetchData("https://api.example.com");    console.log(data);  // Output: Data from https://api.example.com  } catch (error) {    console.error("Error:", error);  }}getData();

This approach combines the benefits of promises with a synchronous style, which makes what might otherwise be relatively complex code all the easier to both read and maintain.


Wrapping up

Understanding the differences between callbacks and promises is essential for effectively handling asynchronous operations in JavaScript. Both approaches have their place, but promises (and, by extension, async/await) offer a more modern, readable, and maintainable solution for most scenarios.

Key Takeaways

  • Callbacks are functions passed as arguments and executed later, but they can lead to callback hell and poor error handling.
  • Promises provide a more structured approach with chaining and centralised error handling.
  • Async/await builds on promises, simplifying asynchronous code further.
  • Choose the right approach based on your project's requirements and complexity.

By mastering callbacks, promises, and async/await, you'll be wellequipped to write robust and efficient asynchronous JavaScript.


Categories:

  1. Development
  2. Front‑End Development
  3. Guides
  4. JavaScript