Javascript Es6 Promises And Async Await Complete Guide

 Last Update:2025-06-22T00:00:00     .NET School AI Teacher - SELECT ANY TEXT TO EXPLANATION.    9 mins read      Difficulty-Level: beginner

Understanding the Core Concepts of JavaScript ES6 Promises and async await

JavaScript ES6 Promises and Async/Await: A Detailed Explanation with Important Information

Understanding Promises

A Promise is an object that represents the eventual completion (or failure) of an asynchronous operation and its resulting value. It's a placeholder object for a value that will be available in the future. Promises have three possible states:

  • Pending: The initial state, neither fulfilled nor rejected.
  • Fulfilled: The operation completed successfully and the promise is resolved with a value.
  • Rejected: The operation failed and the promise is rejected with a reason (typically an error).

Creating a Promise:

const myPromise = new Promise((resolve, reject) => {
  if (someCondition) {
    resolve('Success');
  } else {
    reject('Failure');
  }
});

Consuming a Promise:

Promises can be consumed using the .then() and .catch() methods:

myPromise
  .then((value) => {
    console.log(value); // Output: Success
  })
  .catch((error) => {
    console.log(error); // Output: Failure
  });

Chaining Promises:

Promises can be chained to handle a series of asynchronous operations:

firstPromise()
  .then((value) => secondPromise(value))
  .then((value) => thirdPromise(value))
  .catch((error) => console.log(error));

Async/Await Syntax

async/await provides a cleaner, more readable way to work with Promises. An async function is declared with the async keyword and can use the await keyword to pause the execution until a Promise is resolved.

Declaring an Async Function:

async function fetchData() {
  try {
    let response = await fetch('https://api.example.com/data');
    let data = await response.json();
    console.log(data);
  } catch (error) {
    console.log('Error:', error);
  }
}

Key Advantages of Async/Await:

  • Readability: The code looks and behaves more like synchronous code, making it easier to read and understand.
  • Error Handling: It uses the standard try/catch syntax, eliminating the need for chaining multiple .then() methods.
  • Debugging: Better support in browsers and debugging tools compared to traditional Promise chaining.

Async/Await with Multiple Promises:

You can use Promise.all in conjunction with async/await to handle multiple Promises concurrently:

Online Code run

🔔 Note: Select your programming language to check or run code at

💻 Run Code Compiler

Step-by-Step Guide: How to Implement JavaScript ES6 Promises and async await

Step 1: Understanding Asynchronous Operations

Before diving into Promises and async/await, it's important to understand what asynchronous operations are in JavaScript. An asynchronous operation is one that completes in the future, not immediately. Common examples include network requests, timers, and reading files.

Example of an Asynchronous Operation

console.log("Start");

setTimeout(() => {
  console.log("This prints after a 2-second delay");
}, 2000);

console.log("End");

Output:

Start
End
This prints after a 2-second delay

Step 2: Introduction to Promises

A Promise is an object representing the eventual completion or failure of an asynchronous operation.

Basic Example

let promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("Success: Data retrieved!");
  }, 2000);
});

promise.then((message) => {
  console.log(message);
}).catch((error) => {
  console.error(error);
});

Output:

Success: Data retrieved!

In this example:

  • A new Promise is created with a function that takes two parameters, resolve and reject.
  • Inside the Promise, setTimeout is used to simulate an asynchronous operation.
  • After 2 seconds, resolve is called with a success message.
  • The .then() method is used to handle the resolved value (the success message).
  • The .catch() method is used to handle any errors that might occur (though in this case, we don’t expect any).

Handling Errors

Let's modify the previous example to include an error condition:

let promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    // Simulate an error
    let errorOccurred = true;
    if (errorOccurred) {
      reject("Error: Something went wrong!");
    } else {
      resolve("Success: Data retrieved!");
    }
  }, 2000);
});

promise.then((message) => {
  console.log(message);
}).catch((error) => {
  console.error(error);
});

Output:

Error: Something went wrong!

Step 3: Chaining Multiple Promises

You can chain multiple Promises using .then(). This allows you to perform a series of asynchronous operations in order.

Chaining Example

function fetchData(url) {
  return new Promise((resolve, reject) => {
    console.log(`Fetching data from ${url}`);
    setTimeout(() => {
      resolve({ url, data: "Fetched data!" });
    }, 2000);
  });
}

function processData(dataObj) {
  return new Promise((resolve, reject) => {
    console.log(`Processing data from ${dataObj.url}`);
    setTimeout(() => {
      resolve({ ...dataObj, processedData: `${dataObj.data} - Processed!` });
    }, 1000);
  });
}

fetchData('http://example.com')
  .then((dataObj) => {
    return processData(dataObj);
  })
  .then((finalData) => {
    console.log(finalData.processedData);
  })
  .catch((error) => {
    console.error(error);
  });

Output:

Fetching data from http://example.com
Processing data from http://example.com
Fetched data! - Processed!

In this example:

  • fetchData and processData both return Promises.
  • We chain them using .then(), where the result of one Promise is passed as input to the next.
  • If any Promise is rejected, the error handling is done in the .catch() block.

Step 4: Introduction to async/await

The async/await syntax provides a cleaner way to work with Promises. async functions always return a Promise, and await can only be used inside an async function to wait for a Promise to resolve.

Simple async/await Example

function fetchData(url) {
  return new Promise((resolve, reject) => {
    console.log(`Fetching data from ${url}`);
    setTimeout(() => {
      resolve({ url, data: "Fetched data!" });
    }, 2000);
  });
}

async function performAsyncOperations() {
  try {
    let result = await fetchData('http://example.com');
    console.log(result.data);
  } catch (error) {
    console.error(error);
  }
}

performAsyncOperations();

Output:

Fetching data from http://example.com
Fetched data!

In this example:

  • The performAsyncOperations function is marked as async.
  • Inside the function, we await the fetchData Promise, which means the function execution will pause until fetchData resolves.
  • The try...catch block is used to handle any errors that might occur during the Promise resolution.

Chained async/await Example

We can also use async/await with chained Promises:

function fetchData(url) {
  return new Promise((resolve, reject) => {
    console.log(`Fetching data from ${url}`);
    setTimeout(() => {
      resolve({ url, data: "Fetched data!" });
    }, 2000);
  });
}

function processData(dataObj) {
  return new Promise((resolve, reject) => {
    console.log(`Processing data from ${dataObj.url}`);
    setTimeout(() => {
      resolve({ ...dataObj, processedData: `${dataObj.data} - Processed!` });
    }, 1000);
  });
}

async function performAsyncOperations() {
  try {
    let fetchedData = await fetchData('http://example.com');
    let finalData = await processData(fetchedData);
    console.log(finalData.processedData);
  } catch (error) {
    console.error(error);
  }
}

performAsyncOperations();

Output:

Fetching data from http://example.com
Processing data from http://example.com
Fetched data! - Processed!

Step 5: Concurrent Async Operations

Sometimes, you might want to perform several asynchronous operations concurrently instead of waiting for each one to finish sequentially. You can do this using Promise.all.

Promise.all Example

function fetchData(url) {
  return new Promise((resolve, reject) => {
    console.log(`Fetching data from ${url}`);
    setTimeout(() => {
      resolve({ url, data: `Data from ${url}` });
    }, Math.random() * 2000); // Random delay between 0 and 2 seconds
  });
}

let urls = ['http://api.example.com/first', 'http://api.example.com/second'];

async function fetchAllData() {
  try {
    let results = await Promise.all(urls.map(fetchData));
    results.forEach((result) => {
      console.log(result.data);
    });
  } catch (error) {
    console.error(error);
  }
}

fetchAllData();

Output: (Note that the order of the logged messages may vary due to the random timeout)

Fetching data from http://api.example.com/first
Fetching data from http://api.example.com/second
Data from http://api.example.com/first
Data from http://api.example.com/second

In this example:

  • urls.map(fetchData) creates an array of Promises.
  • Promise.all() waits for all Promises in the array to resolve.
  • Once all Promises resolve, their results are available in the results array.
  • Each result is then logged to the console.

Summary

  • Promises: Objects that represent the eventual completion or failure of an asynchronous operation.
  • Chaining Promises: Using .then() to perform multiple asynchronous operations in sequence.
  • Error Handling: Using .catch() to handle errors for Promises.
  • async/await: Cleaner, synchronous-looking syntax for dealing with Promises. Use await to pause execution of an async function until a Promise resolves.
  • Concurrent Requests: Use Promise.all() when you need to perform multiple asynchronous operations at the same time and wait for all of them to complete.

Top 10 Interview Questions & Answers on JavaScript ES6 Promises and async await

Top 10 Questions and Answers on JavaScript ES6 Promises and async/await

1. What is a Promise in JavaScript?

let promise = new Promise(function(resolve, reject) {
    // do something asynchronous which eventually calls either:
    // resolve(someValue); // fulfilled
    // or
    // reject("failure reason"); // rejected
});

2. How can you create a resolved and rejected Promise?

Answer: To create a resolved (fulfilled) Promise, use Promise.resolve(), and for a rejected Promise, use Promise.reject().

// Resolved promise
const resolvedPromise = Promise.resolve('Success!');

resolvedPromise.then(
    value => console.log(value)  // 'Success!'
);

// Rejected promise
const rejectedPromise = Promise.reject('Oops, something went wrong!');

rejectedPromise.catch(
    reason => console.error(reason)  // 'Oops, something went wrong!'
);

3. How do you handle multiple Promises at once?

Answer: JavaScript provides methods like Promise.all(), Promise.race(), Promise.allSettled(), and Promise.any() for handling multiple Promises concurrently.

  • Promise.all(): When all promises have been resolved, or if at least one promise is rejected, Promise.all() returns a promise that will settle to that outcome.
let promise1 = Promise.resolve(1);
let promise2 = Promise.resolve(2);
let promise3 = Promise.resolve(3);

Promise.all([promise1, promise2, promise3]).then((values) => {
  console.log(values); // Expected output: Array [1, 2, 3]
});
  • Promise.race(): Returns a new Promise that resolves or rejects as soon as any of the Promises in the iterable resolve or reject, with the value or reason from that Promise.
let promise1 = Promise.resolve('Resolved first');

let promise2 = new Promise((resolve, reject) => {
    setTimeout(resolve, 150, 'Resolved second');
});

Promise.race([promise1, promise2]).then((value) => {
    console.log(value);
  // Both resolve, but promise1 resolves first
});

4. What is async/await in JavaScript?

Answer: async/await is syntactic sugar built on top of JavaScript's Promises to make asynchronous code look synchronous. Here's how they work:

  • The async keyword indicates a function is asynchronous and always returns a Promise.

  • The await expression pauses the execution of the async function and waits for the passed Promise's resolution, then resumes the async function's execution and returns the resolved value.

async function fetchData() {
    let response = await fetch('https://api.example.com/data');
    let data = await response.json();
    return data;
}

fetchData().then(data => console.log(data));

5. Can you use try...catch blocks inside async functions?

Answer: Yes, error handling within async functions can be done using try...catch blocks, just as with ordinary synchronous functions. However, it catches errors thrown by await expressions.

async function fetchData() {
    try {
        let response = await fetch('https://api.example.com/data');
        let data = await response.json();
        return data;
    } catch (error) {
        console.error(error.message); // Logs any errors encountered during fetching
    }
}

6. What are the advantages of using async/await over Promises?

Answer: Async/await makes the asynchronous code cleaner and more readable, resembling synchronous code. Advantages include:

  • Sequentiality: Writing sequential code that executes asynchronously can be more intuitive.
  • Try...catch: Error handling becomes easier since all exceptions can be managed via standard try...catch blocks.
  • Debugging: Debugging async/await functions is simpler because of their synchronous flow appearance.

7. Is it possible to call an async function without using .then() or await?

Answer: Yes, an async function can be called directly, but it will return a Promise that needs to be handled to extract the actual returned value. To run an async function without explicit await or .then(), you can use its Promise return value:

async function greet(name) {
    return 'Hello, ' + name + '!';
}

greet('John').then(name => console.log(name)); // Logs 'Hello, John!'

8. Difference between Promise chaining and async/await?

Answer: While both Promise chaining and async/await help manage asynchronous operations, they differ in readability and error handling.

  • Chaining: Involves attaching the .then() and .catch() methods to a Promise. It’s more concise when handling multiple asynchronous functions, but can become messy and hard to follow (often referred to as ‘callback hell’).
getProfile().then(profile => getUserPosts(profile.id))
            .then(posts => console.log(posts))
            .catch(error => console.error(error));
  • Async/Await: The async/await syntax is easier to read and write, especially when dealing with deeply nested asynchronous functions. Errors are handled with try...catch blocks instead of separate chaining.
async function displayUserPosts() {  
    try {
        let profile = await getProfile();
        let posts = await getUserPosts(profile.id);
        console.log(posts);
    } catch (error) {
        console.error(error);
    }
}

9. Can I use async/await with any function, not just those returning promises?

Answer: No, async/await can only be used with functions that return a Promise (or primitive values coerced to promises). Non-Promise returns inside an async function will be automatically wrapped into a resolved Promise.

Attempting to use await outside an async function results in a SyntaxError.

10. When should you use Promises instead of async/await?

Answer: Promises should be used when you want to manage multiple asynchronous operations with constructs like Promise.all(). They may also be preferable in scenarios where you need to perform several sequential asynchronous tasks without nesting too many levels of async functions, as excessive nesting can lead to complex and difficult-to-maintain code.

In some situations, like writing utility libraries, you might design APIs using Promises to offer more flexibility, allowing users to choose whether they want to work with Promises or convert them to async/await.


You May Like This Related .NET Topic

Login to post a comment.