The states of JavaScript promises can be pending, fulfilled, or rejected. Let's look at what you should do when there is an "unhandled promise rejection".
#What is a Promise?
A Promise is a special JavaScript object representing an asynchronous operation's eventual fulfillment or failure. It is similar to making a promise in real life, where you promise to do something in the future. A promise always has two outcomes: you either do it by keeping your Promise or you do not.
JavaScript promises are complex but straightforward to grasp. A promise object has two properties: state and result. The state can be pending, fulfilled, or rejected; the result can be undefined or the value of the fulfilled or rejected state.
Pending
: This is the initial state of the promise when it is being processed. This is the initial state before the promise succeeds or fails and has a result of undefined.Fulfilled
: This is the completed and successful state of a promise. It returns the resolve value as a result.Rejected
: Like the resolved or fulfilled states, the rejected state indicates a failed promise. If a promise ends up in the rejected state, the error value is returned as a result.
In clear terms, the promise state is initially pending with a result of undefined; when the Promise's condition is true, the state is fulfilled and has a result with the value of resolve(value); otherwise, when the condition fails, it has an error value of reject (error).
For example, the code block below is a Promise that checks a condition. If the condition is true it resolves
otherwise, it rejects
.
const myPromise = new Promise((resolve, reject) => {let cms = "Hygraph";if (cms === "Hygraph") {resolve("Success: The promise has successfully resolved!");} else {reject("Failure: The promise has failed!");}});
Editor's Note
settled
.#How to use Promises
function getUsersPromise() {const DUMMY_USERS_DATA = [{ id: 1, name: "John Doe" }];return new Promise((resolve, reject) => {// Fake successsetTimeout(() => {resolve(DUMMY_USERS_DATA);}, 1000);// Fake Error// setTimeout(() => {// reject("something went wrong...");// }, 1000);});}
The getUsersPromise
function returns a promise that simulates fetching user data. The promise uses setTimeout
to delay the resolution by 1 second. When resolved, it returns the DUMMY_USERS_DATA
array. There is also a commented-out section that, if uncommented, would simulate an error by rejecting the promise with the message "something went wrong..." after 1 second.
We can simply use a .then()
handler method to consume a promise.
function getUserList() {return getUsersPromise().then((data) => data);}
We can also use async await
syntax as well to consume a promise.
async function getUserList() {return await getUsersPromise();}
#How to handle unhandled Promise rejections
Many things can get a promise rejected, such as some run time error or a network failure.
When an error arises within a promise, it gets rejected and calls the reject() function. Unhandled promise rejections imply that when a promise is rejected, it is not handled. In other words, it is unhandled because nothing deals with the rejection.
For example:
function getUsersPromise() {const DUMMY_USERS_DATA = [{ id: 1, name: "John Doe" }];return new Promise((resolve, reject) => {// Fake ErrorsetTimeout(() => {reject("something went wrong...");}, 1000);});}async function getUserList() {return await getUsersPromise();}// OR// function getUserList() {// return getUsersPromise().then((data) => data);// }getUserList();
Above is a promise that fails, for simplicity, we have faked and called the reject() function after a delay of 1s. Okay but, what is the big deal about rejected promises and what exactly is an unhandled promise rejection?
The promise getUsersPromise
in the above code is called by the getUserList
function, the main issue here is that if the promise rejects which it will, no code is handling the promise rejection in the getUserList
function. This would generate an “Unhandled Promise Rejection” error. This error can bubble up to your application root and can even stop your entire application.
Also, this is not just applicable to frontend Javascript code, but also to backend Node.js-based codebases.
For example, check this demo video:
In this demo, we have two different routes in a Node.js-based Express application.
- GET /hello
- GET /user
If you notice we hit the /hello
endpoint that says Hello World
. Once we hit the /user
with an unhandled promise, our entire backed app crashes! Our rejected promise had nothing to do with the /hello
endpoint, but it still went down as the app itself crashed. This can be really dangerous in production.
#How to handle Promises correctly
To handle promise rejections specifically, we can use the .catch()
handler method. A more popular option is to wrap the promise call in a try-catch
block.
// Using .catch() handlerfunction getUserList() {return getUsersPromise().then((data) => data).catch((err) => console.error(err));}// Using a try-catch wrapperasync function getUserList() {try {return await getUsersPromise();} catch (err) {console.error(err);}}
That’s as simple as it could be.
Let us also explore an example using the Fetch API. The Fetch API is a built-in JavaScript method for retrieving resources and interacting with your backend server or an API endpoint. It returns a promise, meaning you can use the .then() and .catch() handler methods.
Let’s retrieve data by sending a GraphQL request with Fetch API to the Cocktails Hygraph content repository which holds some cocktails and some information about each cocktail:
fetch("https://api-us-east-1.hygraph.com/v2/cl4ji8xe34tjp01yrexjifxnw/master", {method: "POST",headers: { "Content-Type": "application/json" },body: JSON.stringify({query: `query {cocktails {categoryinfoingredientsinstructionsname}}`,}),}).then((response) => console.log(response.json()));
In the above example, rejection is not handled. In a Fetch API request, the Promise can be rejected due to network issues, permission issues, and other run time reasons. We can attach a catch handler to it to catch any unforeseen issues, as shown below:
fetch("https://api-us-east-1.hygraph.com/v2/cl4ji8xe34tjp01yrexjifxnw/master", {method: "POST",headers: { "Content-Type": "application/json" },body: JSON.stringify({query: `query {cocktails {categoryinfoingredientsinstructionsname}}`})}).then((response) => console.log(response.json())).catch((error) => console.log(error));
Or, we can also use the more popular async await option to have a more friendly syntax.
try {const response = await fetch("https://api-us-east-1.hygraph.com/v2/cl4ji8xe34tjp01yrexjifxnw/master",{method: "POST",headers: { "Content-Type": "application/json" },body: JSON.stringify({query: `query {cocktails {categoryinfoingredientsinstructionsname}}`,}),});console.log(response.json());} catch (err) {console.log(err);}
#Wrapping up
In this article, you have learned what are promises, how to use them. We also saw what exactly is an unhandled promise rejection and how dangerous it can be. Finally, we went through how to handle promises the correct way and saw examples around it.
Feel free to customize and handle rejections of promises however you'd like, but always make sure to handle rejections of promises when working with promises since your web application may run into run-time errors.