Asynchronous Programming: Callbacks, Promises, and Async/Await

Asynchronous vs. Synchronous Programming Synchronous Programming: In synchronous programming, tasks are executed sequentially. The program waits for a task to complete before moving on to the next one. This approach can lead to inefficiencies, especially during time-consuming operations like network requests or file processing. Example of Synchronous Code: function task1() { console.log("Task 1 started"); for (let i = 0; i { const data = "Data received"; // Simulate fetched data callback(data); // Execute the callback with the data }, 2000); } fetchData((data) => { console.log(data); // Logs after data is fetched }); Explanation: The fetchData function simulates a delay using setTimeout. Once the delay ends, the callback is executed with the fetched data. Issues with Callbacks: Callback Hell: When multiple asynchronous operations depend on each other, deeply nested callbacks make the code hard to read and maintain. Promises Definition: A promise is an object representing the eventual completion (or failure) of an asynchronous operation and its resulting value. Benefits: Simplifies chaining asynchronous operations. Provides better error handling compared to callbacks. Example: function fetchData() { return new Promise((resolve, reject) => { console.log("Fetching data..."); setTimeout(() => { const data = "Data received"; resolve(data); // Resolve the promise with data }, 2000); }); } fetchData() .then(data => { console.log(data); // Logs after promise is resolved }) .catch(error => { console.error(error); // Handles errors }); Explanation: Promises improve readability by chaining .then() for success and .catch() for error handling. They eliminate the nested structure typical of callbacks. Async/Await Definition: Async/await is syntactic sugar built on top of promises, enabling you to write asynchronous code that looks and behaves more like synchronous code. Benefits: Improves code readability and maintainability. Handles asynchronous logic more straightforwardly. Example: async function fetchAndLogData() { try { const data = await fetchData(); // Wait for the promise to resolve console.log(data); // Logs after data is fetched } catch (error) { console.error(error); // Handles errors } } await fetchAndLogData(); Explanation: The async keyword indicates the function contains asynchronous code. The await keyword pauses execution until the promise resolves, making the code appear synchronous while remaining non-blocking. Summary of Key Points Synchronous vs. Asynchronous: Synchronous code blocks execution until tasks complete. Asynchronous code allows other tasks to execute concurrently. Callbacks: A function passed to handle asynchronous results. Susceptible to callback hell with complex dependencies. Promises: A cleaner alternative to callbacks, supporting chaining and error handling. Async/Await: Makes asynchronous code more readable and maintainable. Support ❤️ It took a lot of time and effort to create this material. If you found this article useful or interesting, please support my work with a small donation. It will help me to continue sharing my knowledge and ideas. Make a contribution or Subscription to the author's content: Buy me a Coffee, Patreon, PayPal.

Apr 14, 2025 - 19:24
 0
Asynchronous Programming: Callbacks, Promises, and Async/Await

Asynchronous vs. Synchronous Programming

Synchronous Programming:

  • In synchronous programming, tasks are executed sequentially. The program waits for a task to complete before moving on to the next one.
  • This approach can lead to inefficiencies, especially during time-consuming operations like network requests or file processing.

Example of Synchronous Code:

function task1() {
  console.log("Task 1 started");
  for (let i = 0; i < 1e9; i++); // Simulating a long task
  console.log("Task 1 completed");
}

function task2() {
  console.log("Task 2 started");
  for (let i = 0; i < 1e9; i++); // Simulating a long task
  console.log("Task 2 completed");
}

task1(); // Executes first
task2(); // Executes after task1 is completed

Asynchronous Programming:

  • Asynchronous programming allows tasks to run independently, enabling the program to continue executing other operations while waiting for some tasks to complete.
  • This is especially useful for tasks like fetching data from a server, where waiting could otherwise freeze the user interface.

Why Do We Need Asynchronous Programming?

  1. Responsiveness: Prevents the UI from becoming unresponsive during long operations.
  2. Efficiency: Allows simultaneous execution of multiple operations, optimizing resource usage.
  3. User Experience: Enhances interactions by ensuring smooth and uninterrupted workflows.

Callbacks

Definition: A callback is a function passed as an argument to another function, executed after the completion of a task.

Example:

function fetchData(callback) {
  console.log("Fetching data...");

  setTimeout(() => {
    const data = "Data received"; // Simulate fetched data
    callback(data); // Execute the callback with the data
  }, 2000);
}

fetchData((data) => {
  console.log(data); // Logs after data is fetched
});

Explanation:

  • The fetchData function simulates a delay using setTimeout.
  • Once the delay ends, the callback is executed with the fetched data.

Issues with Callbacks:

  • Callback Hell: When multiple asynchronous operations depend on each other, deeply nested callbacks make the code hard to read and maintain.

Promises

Definition: A promise is an object representing the eventual completion (or failure) of an asynchronous operation and its resulting value.

Benefits:

  • Simplifies chaining asynchronous operations.
  • Provides better error handling compared to callbacks.

Example:

function fetchData() {
  return new Promise((resolve, reject) => {
    console.log("Fetching data...");

    setTimeout(() => {
      const data = "Data received";
      resolve(data); // Resolve the promise with data
    }, 2000);
  });
}

fetchData()
  .then(data => {
    console.log(data); // Logs after promise is resolved
  })
  .catch(error => {
    console.error(error); // Handles errors
  });

Explanation:

  • Promises improve readability by chaining .then() for success and .catch() for error handling.
  • They eliminate the nested structure typical of callbacks.

Async/Await

Definition: Async/await is syntactic sugar built on top of promises, enabling you to write asynchronous code that looks and behaves more like synchronous code.

Benefits:

  • Improves code readability and maintainability.
  • Handles asynchronous logic more straightforwardly.

Example:

async function fetchAndLogData() {
  try {
    const data = await fetchData(); // Wait for the promise to resolve
    console.log(data); // Logs after data is fetched
  } catch (error) {
    console.error(error); // Handles errors
  }
}

await fetchAndLogData();

Explanation:

  • The async keyword indicates the function contains asynchronous code. The await keyword pauses execution until the promise resolves, making the code appear synchronous while remaining non-blocking.

Summary of Key Points

  1. Synchronous vs. Asynchronous:
    • Synchronous code blocks execution until tasks complete.
    • Asynchronous code allows other tasks to execute concurrently.
  2. Callbacks:
    • A function passed to handle asynchronous results.
    • Susceptible to callback hell with complex dependencies.
  3. Promises:
    • A cleaner alternative to callbacks, supporting chaining and error handling.
  4. Async/Await:
    • Makes asynchronous code more readable and maintainable.

Support ❤️

It took a lot of time and effort to create this material. If you found this article useful or interesting, please support my work with a small donation. It will help me to continue sharing my knowledge and ideas.

Make a contribution or Subscription to the author's content: Buy me a Coffee, Patreon, PayPal.