1. Home
image

JavaScript Tutorial Concepts - From Beginner to Pro

Learn JavaScript from scratch! Our tutorial covers basics to advanced concepts. Start learning today!

  • 24
  • 9 Hours
right-top-arrow
13

JavaScript Promises

Updated on 08/08/2024449 Views

I believe that JavaScript's asynchronous nature, while powerful, often leads to convoluted code littered with nested callbacks. This compromises readability, error handling, and maintainability. I know this from prior experience handling unorganized chunks of asynchronous code.

Callbacks within callbacks formed a labyrinth in my projects. According to me, error handling transformed into a game of chance, and readability was extremely low.

JavaScript promises offer a solution for all of these. They provide a structured approach to managing the results of asynchronous operations, promoting cleaner code and more reliable asynchronous workflows. Promises encapsulate success and failure states, allowing for consistent error handling and the ability to chain asynchronous actions in a more intuitive manner.

If you're looking to streamline your JavaScript code and gain a stronger grasp of asynchronous patterns, understanding promises is crucial. Let us learn more with this JavaScript promise tutorial.

What are JavaScript Promises?

In JavaScript, promises are objects designed to represent the eventual outcome of an asynchronous operation. This eventual outcome can be either a successful result or an error.

Promises have three primary states:

  • Pending: The initial state of a promise, before the asynchronous operation has been completed.
  • Fulfilled: The promise has been successfully resolved, and the resulting value is available.
  • Rejected: The asynchronous operation has failed, and an error reason is available.

Why Use JavaScript Promises?

Promises provide a significant improvement over traditional callback-based approaches for handling asynchronous code in JavaScript.

Here's why:

  • Enhanced readability: Promises make it easier to follow the flow of asynchronous logic by avoiding deeply nested callbacks.
  • Improved error handling: Promises have a built-in mechanism for error handling, making it easier to catch and manage errors in asynchronous chains.
  • Avoiding 'Callback Hell': Promises help avoid complex nesting of callbacks, creating more organized and manageable code, especially for multiple asynchronous operations.

Creating JavaScript Promises

Using JavaScript promises is extremely simple. To newly create a JavaScript promise, you utilize the new Promise() constructor. This constructor function accepts a single argument, an executor function that takes two callback parameters, customarily named resolve and reject.

The executor function is where you place the code responsible for the asynchronous operation. The resolve function is called when the operation completes successfully. The reject function is called if the operation fails.

Successful JavaScript promise example:

const myPromise = new Promise((resolve, reject) => {

  setTimeout(() => resolve("Success!"), 1000); // Simulate a 1-second delay

}); 

Failed JavaScript promise example:

const myPromise = new Promise((resolve, reject) => {

  const error = new Error("Something went wrong");

  reject(error); 

});

Common Applications of JavaScript Promises

The JavaScript promise method has many practical applications. Let us check out some common uses of the JavaScript promise function.

Wrapping AJAX Requests

Example:

function fetchData(url) {

  return new Promise((resolve, reject) => {

    const xhr = new XMLHttpRequest();

    xhr.open('GET', url);

    xhr.onload = () => resolve(xhr.responseText);

    xhr.onerror = () => reject(new Error('Network Error')); 

    xhr.send();

  });

}

Wrapping Timers

Example:

function delay(time) {

  return new Promise((resolve, reject) => {

    setTimeout(resolve, time); 

  });

}

Wrapping User Input

Example:

function getUserInput(promptMessage) {

   return new Promise((resolve, reject) => {

     const inputValue = prompt(promptMessage);

     if (inputValue) {

       resolve(inputValue); 

     } else {

       reject(new Error('User did not provide input'));

     }

   });

}

Practical JavaScript Promises Examples and Projects

Let us check out some practical Javascript promise object examples and projects that you can try out yourself.

Creating a Promise-Based Image Loader

index.html code:

<!DOCTYPE html>

<html lang="en">

<head>

  <meta charset="UTF-8">

  <meta name="viewport" content="width=device-width, initial-scale=1.0">

  <title>Promise Image Loader</title>

  <link rel="stylesheet" href="styles.css"> 

</head>

<body>

  <div id="image-container"></div>  

  <script src="script.js"></script> 

</body>

</html>

styles.css code:

#image-container {

  display: flex;

  justify-content: center;

  align-items: center;

  min-height: 300px;

  border: 1px solid #ccc; 

}

#image-container .error-message {

  color: red;

}

script.js code:

function loadImage(imageUrl) {

  return new Promise((resolve, reject) => {

    const image = new Image();

    image.onload = () => resolve(image);

    image.onerror = (error) => reject(error);

    image.src = imageUrl;

  });

}

loadImage('https://example.com/myimage.jpg')

  .then((image) => {

    document.body.appendChild(image); 

  })

  .catch((error) => {

    console.error("Error loading image:", error);

  });

Building a Promise Chain

index.html code:

<!DOCTYPE html>

<html lang="en">

<head>

  <meta charset="UTF-8">

  <meta name="viewport" content="width=device-width, initial-scale=1.0">

  <title>Promise Delay</title>

  <link rel="stylesheet" href="styles.css"> 

</head>

<body>

  <div id="output"></div>

  <script src="script.js"></script> 

</body>

</html>

styles.css code:

#output {

  min-height: 80px;

  border: 1px solid #ddd;

  padding: 10px; 

}

script.js code:

function delay(time) {

  return new Promise((resolve) => setTimeout(resolve, time));

}

const outputDiv = document.getElementById('output');

delay(1000)

 .then(() => {

   outputDiv.textContent = "After 1 second"; 

   return delay(2000);

 })

 .then(() => {

   outputDiv.textContent = "After 3 seconds total"; 

 });

Fetching Data with JavaScript Promises

Let's refactor an AJAX request using callbacks to a cleaner promise-based approach with .then().

Callback approach:

function fetchDataWithCallback(url, callback) {

  const xhr = new XMLHttpRequest();

  xhr.open('GET', url);

  xhr.onload = function() {

    callback(null, xhr.responseText);

  };

  xhr.onerror = function() {

    callback(new Error('Network Error'), null);

  };

  xhr.send();

}

fetchDataWithCallback('https://api.upGrad.com/data', (error, data) => {

  if (error) {

    console.error(error);

  } else {

    console.log(data);

  }

});

Promise-based approach:

function fetchDataWithPromise(url) {

  return new Promise((resolve, reject) => {

    const xhr = new XMLHttpRequest();

    xhr.open('GET', url);

    xhr.onload = function() {

      resolve(xhr.responseText);

    };

    xhr.onerror = function() {

      reject(new Error('Network Error'));

    };

    xhr.send();

  });

}

fetchDataWithPromise('https://api.upGrad.com/data')

  .then((data) => {

    console.log(data);

  })

  .catch((error) => {

    console.error(error);

  });

This promise-based approach offers improved readability, easier error handling, and the ability to chain further asynchronous operations using .then().

Working With JavaScript Promises Using .then() and .catch()

.then() Method

The .then() method is used to chain a callback function that executes when a promise is successfully fulfilled (resolved). .then() takes a callback function as an argument, which receives the resolved value from the promise. Also, .then() returns a new promise, allowing you to chain multiple .then() calls for sequential asynchronous operations.

Example:

myPromise.then((result) => {

    console.log(result); // Will log "Success!" after 1 second

}).catch((error) => {

    // This will not be called since myPromise was successful

}); 

.catch() Method

The .catch() method is used to chain a callback function that executes when a promise is rejected. .catch() takes a callback function as an argument, which receives the error object from the rejected promise. This method is crucial for error handling and prevents errors from going unnoticed in promise chains.

Example:

myPromise.then((result) => {

    // ... do something with result

}).catch((error) => {

    console.error("An error occurred:", error); 

});


We can chain multiple .then() calls to handle a sequence of successful asynchronous operations. I would also like to mention that any error in a .then() chain will be caught by the next .catch() or by a global error handler if no .catch() is present.

Returning Promises from .then()

When you return a promise from within a .then() callback, the next .then() in the chain will wait for that returned promise to resolve before executing. This is the foundation of composing asynchronous sequences.

Example:

function getUserData() {

  return new Promise((resolve) => {

    setTimeout(() => resolve({ userId: 1 }), 1000); // Simulate fetching user data

  });

}

function fetchUserPosts(userId) {

  return new Promise((resolve) => {

    setTimeout(() => resolve(['Post 1', 'Post 2']), 500); // Simulate fetching posts

  });

}

function displayPosts(posts) {

  console.log(posts); 

}

// Chaining asynchronous operations:

getUserData()

  .then((userData) => fetchUserPosts(userData.userId)) 

  .then(displayPosts)

  .catch((error) => console.error("An error occurred:", error));

In the above example, getUserData simulates an asynchronous operation to fetch user data. It returns a promise. The first .then() receives the resolved userData. We return a new promise, fetchUserPosts, using the userData.userId.

The second .then() waits for fetchUserPosts to resolve. It then passes the resulting posts to displayPosts. Finally, the .catch() handles errors anywhere in the chain. When we are composing promise chains, we should always handle potential errors with either .catch() or by returning rejected promises from within your .then() callbacks.

Advanced Promise Concepts

Promise.all()

Promise.all() executes a set of promises concurrently and returns a single promise that resolves when all input promises have resolved.

Promise.all() takes an iterable of promises (array, etc.) as input and returns a new promise that resolves to an array containing the resolved values of all input promises (in the same order). This method also rejects if any of the input promises are rejected, with the rejection reason being the error from the first rejected promise.

Syntax: Promise.all(iterableOfPromises)

Example:

const promise1 = new Promise((resolve, reject) => {

  setTimeout(() => resolve("Promise 1 resolved"), 1000);

});

const promise2 = new Promise((resolve, reject) => {

  setTimeout(() => resolve("Promise 2 resolved"), 2000);

});

Promise.all([promise1, promise2])

  .then((results) => {

    console.log(results); // ["Promise 1 resolved", "Promise 2 resolved"]

  })

  .catch((error) => {

    console.error(error);

  });

Promise.race()

Promise.race() executes a set of promises concurrently and returns a single promise that resolves or rejects as soon as any of the input promises resolves or rejects.

Promise.race() takes an iterable of promises as input and returns a new promise that resolves or rejects based on the first settled input promise (whichever resolves or rejects first) This method resolves with the resolved value of the first resolved promise and rejects with the rejection reason of the first rejected promise.

Syntax: Promise.race(iterableOfPromises)

Example:

const promise1 = new Promise((resolve, reject) => {

  setTimeout(() => resolve("Promise 1 resolved"), 1000);

});

const promise2 = new Promise((resolve, reject) => {

  setTimeout(() => reject(new Error("Promise 2 rejected")), 500);

});

Promise.race([promise1, promise2])

  .then((result) => {

    console.log(result); // Will not be called because promise2 rejects first

  })

  .catch((error) => {

    console.error(error); // Logs "Error: Promise 2 rejected"

  });

Async/Await with JavaScript Promises

Async/Await functions always return a promise, and the await keyword can only be used inside functions declared with async. Async/Await is primarily a way to make asynchronous code look and behave more like synchronous code, even though it's still asynchronous under the hood. 

There are two key components:

1. async function: You declare a function as async to enable the use of the await keyword within it.

Syntax example:

let result = await somePromiseReturningFunction();

2. await keyword:  The await keyword can be placed in front of a promise-returning function. It pauses the execution of the async function until the promise is resolved.  The await expression returns the resolved value of the promise.

Syntax example:

let result = await somePromiseReturningFunction();

If you wish to master JS and become a full stack developer, you can check out upGrad’s full stack development courses.

Wrapping Up

I hope that I got the fundamentals of JavaScript promises explained to you properly in this tutorial. If you wish to learn more about JS, you can check out the other JS tutorials, or, you can simply join upGrad’s software development courses to start mastering all the essential tools.

Frequently Asked Questions

1. What is a JavaScript promise?
A JavaScript promise is an object representing the eventual completion (success or failure) of an asynchronous operation.

2. What are the 3 promises in JavaScript?

- Pending: The initial state, before completion or failure.

- Fulfilled: The operation completed successfully, and a result is available.

- Rejected: The operation failed, and an error reason is available.

3. What is an example of a promise?

const myPromise = new Promise((resolve, reject) => {

  // Simulate fetching request with a timeout

  setTimeout(() => {

    resolve("Fetched data successfully!"); 

  }, 2000); 

});

4. Why promise is better than callback?
Promises improve asynchronous code readability, provide built-in error handling mechanisms, and simplify chaining of asynchronous operations, avoiding the "callback hell" problem.

5. Why use JavaScript promises?
Use JavaScript promises to manage asynchronous operations in a structured way, enhance error management, and make your code more maintainable.

6. What is async and Promise in JavaScript?

  • Promise: A mechanism for handling the results of asynchronous operations.
  • Async: A keyword that declares a function as asynchronous, allowing the use of the await keyword within it to pause execution until a promise resolves.

7. What is callback in JS?
A callback in JS is a function passed as an argument to another function, to be executed later after an event or task completes.

8. Is async better than Promise?
Async/await is syntactic sugar built upon promises, making asynchronous code look more synchronous; it's not about "better" but rather, about cleaner style in many scenarios.

image

mukesh

Working with upGrad as a Senior Engineering Manager with more than 10+ years of experience in Software Development and Product Management.

Get Free Career Counselling
form image
+91
*
By clicking, I accept theT&Cand
Privacy Policy
image
Join 10M+ Learners & Transform Your Career
Learn on a personalised AI-powered platform that offers best-in-class content, live sessions & mentorship from leading industry experts.
right-top-arrowleft-top-arrow

upGrad Learner Support

Talk to our experts. We’re available 24/7.

text

Indian Nationals

1800 210 2020

text

Foreign Nationals

+918045604032

Disclaimer

upGrad does not grant credit; credits are granted, accepted or transferred at the sole discretion of the relevant educational institution offering the diploma or degree. We advise you to enquire further regarding the suitability of this program for your academic, professional requirements and job prospects before enr...