Asynchronous Programming in Javascript

David GostinDavid Gostin
3 min read

Here are some examples of asynchronous programming in JavaScript using callbacks, Promises, and async/await.

1. Using Callbacks

A callback is a function passed as an argument to another function, which is then executed after some operation completes. This approach was common in early JavaScript for handling asynchronous tasks, like data fetching.

Example: Fetching data with a callback

function fetchData(callback) {
  setTimeout(() => {
    const data = "Some fetched data";
    callback(data);
  }, 2000); // simulating network delay
}

fetchData((data) => {
  console.log("Data received:", data);
});

In this example, fetchData takes a callback function as an argument. After a delay, it calls the callback function with the fetched data.

2. Using Promises

Promises provide a cleaner way to handle asynchronous operations by avoiding "callback hell" (deeply nested callbacks). They have three states: pending, fulfilled, and rejected.

Example: Fetching data with a Promise

function fetchData() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      const data = "Some fetched data";
      resolve(data); // fulfill the promise
    }, 2000);
  });
}

fetchData()
  .then((data) => {
    console.log("Data received:", data);
  })
  .catch((error) => {
    console.error("Error:", error);
  });

In this example, fetchData returns a Promise. We use .then() to handle the data when the promise is fulfilled, and .catch() to handle any errors if the promise is rejected.

3. Using async/await

async/await syntax, introduced in ES8, makes asynchronous code look and behave more like synchronous code, improving readability. It works on top of Promises and allows you to "await" the completion of a promise within an async function.

Example: Fetching data with async/await

function fetchData() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      const data = "Some fetched data";
      resolve(data);
    }, 2000);
  });
}

async function getData() {
  try {
    const data = await fetchData(); // wait for the promise to resolve
    console.log("Data received:", data);
  } catch (error) {
    console.error("Error:", error);
  }
}

getData();

In this example, the getData function is marked as async, allowing us to use await to pause the function until fetchData resolves. This results in a more readable and manageable way to handle asynchronous code.

4. Real-World Example: Fetching Data from an API

Here’s how you might fetch data from an API using fetch with async/await:

async function fetchFromAPI() {
  try {
    const response = await fetch("https://jsonplaceholder.typicode.com/posts/1");
    if (!response.ok) {
      throw new Error("Network response was not ok");
    }
    const data = await response.json();
    console.log("Data from API:", data);
  } catch (error) {
    console.error("Fetch error:", error);
  }
}

fetchFromAPI();

In this example:

  1. We use await fetch(url) to wait for the API call to complete.

  2. We check if the response is okay (response.ok). If not, we throw an error.

  3. If successful, we parse the response data with .json() and log it to the console.

0
Subscribe to my newsletter

Read articles from David Gostin directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

David Gostin
David Gostin

Full-Stack Web Developer with over 25 years of professional experience. I have experience in database development using Oracle, MySQL, and PostgreSQL. I have extensive experience with API and SQL development using PHP and associated frameworks. I am skilled with git/github and CI/CD. I have a good understanding of performance optimization from the server and OS level up to the application and database level. I am skilled with Linux setup, configuration, networking and command line scripting. My frontend experience includes: HTML, CSS, Sass, JavaScript, jQuery, React, Bootstrap and Tailwind CSS. I also have experience with Amazon EC2, RDS and S3.