Logo
READLEARNKNOWCONNECT
Back to Lessons

    Page

  • - Why Asynchronous JavaScript Matters
  • - Callbacks
  • - Promises
  • - Chaining Promises
  • - Async/Await
  • - Error Handling in Async Code
  • - Parallel vs Sequential Execution
  • - Mini Challenge

18. Asynchronous JavaScript Deep Dive

Level: IntermediateDuration: 40m

Why Asynchronous JavaScript Matters

Modern web apps need to perform tasks that take time — fetching data, reading files, or interacting with APIs. JavaScript handles these with asynchronous patterns, so your app stays responsive while waiting for results.

Callbacks

A callback is a function passed into another function to run later, often after an asynchronous operation completes.

javascript
function fetchData(callback) {
  setTimeout(() => {
    callback('Data loaded');
  }, 1000);
}

fetchData((result) => {
  console.log(result); // Data loaded
});

While callbacks work, they can lead to nested 'callback hell' for complex sequences.

Promises

Promises represent a value that may be available now, later, or never. They provide `.then()` for success and `.catch()` for errors.

javascript
const promise = new Promise((resolve, reject) => {
  const success = true;
  setTimeout(() => {
    if(success) resolve('Data received');
    else reject('Error occurred');
  }, 1000);
});

promise
  .then(result => console.log(result))
  .catch(error => console.error(error));

Chaining Promises

Promises can be chained to perform multiple asynchronous operations in order, avoiding deeply nested callbacks.

javascript
fetch('https://jsonplaceholder.typicode.com/users')
  .then(response => response.json())
  .then(users => {
    console.log(users.length);
    return users[0];
  })
  .then(user => console.log(user.name))
  .catch(error => console.error('Error fetching data:', error));

Async/Await

Async functions let you write asynchronous code that looks synchronous. Use `await` to pause until a Promise resolves.

javascript
async function getUser() {
  try {
    const response = await fetch('https://jsonplaceholder.typicode.com/users/1');
    const user = await response.json();
    console.log(user.name);
  } catch (error) {
    console.error('Error:', error);
  }
}

getUser();

Error Handling in Async Code

Always handle errors in asynchronous code using `.catch()` with Promises or `try...catch` with async/await. Network issues, invalid URLs, or bad responses can throw errors.

javascript
async function fetchWithErrorHandling() {
  try {
    const response = await fetch('https://jsonplaceholder.typicode.com/invalid-url');
    if(!response.ok) throw new Error('Network response was not ok');
    const data = await response.json();
    console.log(data);
  } catch (error) {
    console.error('Caught error:', error.message);
  } finally {
    console.log('Fetch attempt finished');
  }
}

fetchWithErrorHandling();

Parallel vs Sequential Execution

Use `Promise.all` to run multiple async operations in parallel, or `await` sequentially if order matters.

javascript
async function fetchMultiple() {
  const urls = [
    'https://jsonplaceholder.typicode.com/users/1',
    'https://jsonplaceholder.typicode.com/users/2'
  ];

  // Parallel
  const results = await Promise.all(urls.map(url => fetch(url).then(res => res.json())));
  console.log(results);

  // Sequential
  for(const url of urls) {
    const res = await fetch(url);
    const data = await res.json();
    console.log(data.name);
  }
}

fetchMultiple();

Mini Challenge

1. Fetch a list of users from the Random User API and log their first and last names. 2. Use async/await with try/catch to handle errors. 3. Fetch 3 separate endpoints in parallel and display results. 4. Demonstrate what happens if one of the Promises fails using Promise.all.

MDN Docs: Promises

MDN Docs: Async/Await