17.3 Programming Activity: Asynchronous Programming Practices (20 mins)

Objective:

Illustrate key concepts of asynchronous programming by writing JavaScript programs that simulate real-world tasks using callbacks, promises, and async/await.

Students will practice:

  1. Writing functions to perform asynchronous tasks.

  2. Implementing callbacks, promises, and async/await.

  3. Comparing the readability and usability of each approach.


Task: Simulate a Bookstore Operations

Simulate the following asynchronous operations:

  1. Get list of books: Fetch a list of available books (simulate with a delay).

  2. Get book details: Fetch details of a selected book (simulate with a delay).

  3. Place an order: Place an order for the selected book (simulate with a delay).

Students will implement these operations using:

  • Callbacks

  • Promises

  • Async/Await


Step-by-Step Procedures

1. Setup the Asynchronous Task Functions

Define three asynchronous functions using setTimeout to simulate delays:

function getBooks(callback) {
  setTimeout(() => {
    callback(null, ['Book 1', 'Book 2', 'Book 3']);
  }, 1000); // Simulates fetching books
}

function getBookDetails(book, callback) {
  setTimeout(() => {
    if (!book) {
      callback('Book not found', null);
    } else {
      callback(null, { title: book, author: 'Author Name', price: 20 });
    }
  }, 1500); // Simulates fetching book details
}

function placeOrder(bookDetails, callback) {
  setTimeout(() => {
    if (!bookDetails) {
      callback('Invalid book details', null);
    } else {
      callback(null, `Order placed for ${bookDetails.title}`);
    }
  }, 2000); // Simulates placing an order
}

2. Task 1: Using Callbacks

Implement the operations using nested callbacks:

  1. Fetch the list of books.

  2. Select a book and fetch its details.

  3. Place an order for the selected book.

Solution:

getBooks((err, books) => {
  if (err) {
    console.error(err);
    return;
  }
  console.log('Books:', books);

  const selectedBook = books[1]; // Select the second book
  getBookDetails(selectedBook, (err, bookDetails) => {
    if (err) {
      console.error(err);
      return;
    }
    console.log('Book Details:', bookDetails);

    placeOrder(bookDetails, (err, orderMessage) => {
      if (err) {
        console.error(err);
        return;
      }
      console.log(orderMessage);
    });
  });
});

3. Task 2: Refactor Using Promises

Modify the task functions to return promises instead of using callbacks:

function getBooks() {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve(['Book 1', 'Book 2', 'Book 3']);
    }, 1000);
  });
}

function getBookDetails(book) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (!book) {
        reject('Book not found');
      } else {
        resolve({ title: book, author: 'Author Name', price: 20 });
      }
    }, 1500);
  });
}

function placeOrder(bookDetails) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (!bookDetails) {
        reject('Invalid book details');
      } else {
        resolve(`Order placed for ${bookDetails.title}`);
      }
    }, 2000);
  });
}

Solution Using Promises:

getBooks()
  .then((books) => {
    console.log('Books:', books);
    return getBookDetails(books[1]); // Select the second book
  })
  .then((bookDetails) => {
    console.log('Book Details:', bookDetails);
    return placeOrder(bookDetails);
  })
  .then((orderMessage) => {
    console.log(orderMessage);
  })
  .catch((error) => {
    console.error(error);
  });

4. Task 3: Refactor Using Async/Await

Use async/await to make the code more readable and linear.

Solution Using Async/Await:

async function processOrder() {
  try {
    const books = await getBooks();
    console.log('Books:', books);

    const bookDetails = await getBookDetails(books[1]); // Select the second book
    console.log('Book Details:', bookDetails);

    const orderMessage = await placeOrder(bookDetails);
    console.log(orderMessage);
  } catch (error) {
    console.error(error);
  }
}

processOrder();

Comparing the Techniques

Technique
Pros
Cons

Callbacks

Simple and easy for one or two tasks.

Callback hell for nested tasks.

Promises

Handles chaining and errors cleanly.

Slightly more verbose than async/await.

Async/Await

Linear and synchronous-like code flow.

Requires modern JavaScript support.


Expected Outcomes:

  1. Students will understand the differences in readability and usability of callbacks, promises, and async/await.

  2. Students will be able to simulate asynchronous operations and handle errors effectively.

  3. Gain hands-on experience in refactoring asynchronous code for improved maintainability.

Last updated