17.1 Asynchronous Programming Techniques (25 mins)

1. The Importance of Asynchronous Programming

Asynchronous programming allows JavaScript to:

  • Perform non-blocking operations, keeping the UI responsive.

  • Handle tasks like API calls, file reading, or animations without freezing the main thread.

  • Manage multiple simultaneous operations, such as fetching multiple datasets concurrently.


2. Common Asynchronous Programming Techniques

2.1 Using Callbacks

Scenario: You want to perform a task after another finishes.

Steps:

  1. Define a function and pass another function (callback) as an argument.

  2. Invoke the callback function when the task is complete.

Example:

function fetchData(callback) {
  setTimeout(() => {
    const data = 'Data fetched!';
    callback(data);
  }, 2000); // Simulate a delay of 2 seconds
}

fetchData((result) => {
  console.log(result); // Logs: "Data fetched!"
});

2.2 Using Promises

Promises simplify handling multiple asynchronous operations and chaining them in a more readable manner.

Steps:

  1. Use a Promise constructor to wrap an asynchronous task.

  2. Use .then() to handle success and .catch() to handle errors.

Example:

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

fetchData()
  .then((result) => {
    console.log(result); // Logs: "Data fetched!"
  })
  .catch((error) => {
    console.error(error);
  });

Key Points:

  • resolve: Called when the operation succeeds.

  • reject: Called when the operation fails.


2.3 Using Async/Await

Async/Await allows for writing asynchronous code in a synchronous style.

Steps:

  1. Define a function with the async keyword.

  2. Use the await keyword to pause execution until the promise resolves.

Example:

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

async function processData() {
  try {
    const result = await fetchData();
    console.log(result); // Logs: "Data fetched!"
  } catch (error) {
    console.error(error);
  }
}

processData();

3. Combining Techniques

  • Chaining Promises: When multiple tasks depend on each other.

function fetchData1() {
  return new Promise((resolve) => setTimeout(() => resolve('Data 1'), 1000));
}

function fetchData2() {
  return new Promise((resolve) => setTimeout(() => resolve('Data 2'), 2000));
}

fetchData1()
  .then((result1) => {
    console.log(result1); // Logs: "Data 1"
    return fetchData2();
  })
  .then((result2) => {
    console.log(result2); // Logs: "Data 2"
  });
  • Parallel Execution with Promise.all: Use Promise.all to execute multiple asynchronous tasks in parallel and wait for all of them to complete.

function fetchData1() {
  return new Promise((resolve) => setTimeout(() => resolve('Data 1'), 1000));
}

function fetchData2() {
  return new Promise((resolve) => setTimeout(() => resolve('Data 2'), 2000));
}

Promise.all([fetchData1(), fetchData2()])
  .then((results) => {
    console.log(results); // Logs: ["Data 1", "Data 2"]
  });

Student Activity

Objective: Practice and demonstrate the use of callbacks, promises, and async/await.


Activity 1: Chaining Promises

Task:

  1. Create two functions, getUserDetails and getUserPosts, that simulate fetching user details and their posts.

  2. Use promise chaining to first fetch user details, then their posts.

Template:

function getUserDetails() {
  return new Promise((resolve) => setTimeout(() => resolve({ id: 1, name: 'Alice' }), 1000));
}

function getUserPosts(userId) {
  return new Promise((resolve) =>
    setTimeout(() => resolve(['Post 1', 'Post 2', 'Post 3']), 2000)
  );
}

getUserDetails()
  .then((user) => {
    console.log(`User: ${user.name}`);
    return getUserPosts(user.id);
  })
  .then((posts) => {
    console.log('Posts:', posts);
  })
  .catch((error) => {
    console.error(error);
  });

Activity 2: Refactoring with Async/Await

Task: Refactor the solution from Activity 1 to use async/await.

Template:

function getUserDetails() {
  return new Promise((resolve) => setTimeout(() => resolve({ id: 1, name: 'Alice' }), 1000));
}

function getUserPosts(userId) {
  return new Promise((resolve) =>
    setTimeout(() => resolve(['Post 1', 'Post 2', 'Post 3']), 2000)
  );
}

async function displayUserDetails() {
  try {
    const user = await getUserDetails();
    console.log(`User: ${user.name}`);
    const posts = await getUserPosts(user.id);
    console.log('Posts:', posts);
  } catch (error) {
    console.error(error);
  }
}

displayUserDetails();

Expected Learning Outcomes:

  • Understand how to chain asynchronous operations using promises.

  • Use async/await to simplify asynchronous workflows.

  • Recognize scenarios where parallel execution (Promise.all) is useful.

Last updated