Improve Article
Save Article
Improve Article
Save Article
This article covers the use of reject and throw premises in Javascript and explains it’s differences.
reject(): It is an inbuilt function in Javascript that returns a Promise object which has been rejected for a particular given reason.
Syntax:
Promise.reject(reason)
Examples: The reason can be a simple string message or you can even pass an Error object.
- Program 1: Passing a string message as the reason.
javascript
<script>
const p =
new
Promise( ( resolve, reject ) => {
reject(
'promise failed!'
);
});
p.
catch
(err => {
console.log( err );
});
</script>
- Output:
promise failed!
- Program 2: Passing an instanceOf Error as reason.
javascript
<script>
const p =
new
Promise( ( resolve, reject ) => {
reject(
new
Error(
'promise failed!'
) );
});
p.
catch
( err => {
console.log( err );
});
</script>
- Output: As you can see when we are passing an Error object we get the entire Error tree. So it is upto the user which one the user prefers.
Error: promise failed! at :4:9 at new Promise () at :2:11 at render (tryit.php:202) at tryit.php:170 at dispatch (jquery.js:4435) at r.handle (jquery.js:4121)
throw: It is used in JavaScript to create and throw user defined exceptions. Using JavaScript throw statement, you can completely control program flow and generate the user define error messages. If we use throw instead of reject() in the above two examples the results will be exactly same (you can try it yourself just by replacing reject with throw).
Examples: However throw can be used in any Javascript try-catch block and not only with promises.
- Program 1: Using throw in a promise.
javascript
<script>
const p =
new
Promise( ( resolve, reject ) => {
throw
(
'promise failed!'
);
});
p.
catch
(err => {
console.log( err );
});
</script>
- Output:
promise failed!
- Program 2: Using throw without a promise.
javascript
<script>
var
a = 20;
try
{
if
( a < 25 )
throw
(
'Less than 25'
);
console.log(
'Okay!'
);
}
catch
(err)
{
console.log( err );
}
</script>
- Output: Now as we have understood the basic working of both reject and throw, let us talk about the differences between them:
Less than 25
Comparison between Promise- reject and throw:
1. If there is an asynchronous callback function inside the Promise then we cannot use throw from inside the callback function as it will not be recognised by catch() and we will be getting an error in the output.
- Program 1:
javascript
<script>
const p =
new
Promise( ( resolve, reject ) => {
setTimeout( () => {
throw
(
'promise failed!'
);
}, 1000);
});
p.
catch
( ( err )=> {
console.log( err );
});
</script>
- Output: As you can see the error message (“promise failed!”) has been printed in the output but it wasn’t printed by the catch() function of our promise. It becomes an uncaught exception.
/home/akarshan/Desktop/Projects/Personal/gfg/app.js:3 throw( 'promise failed!' ); ^ promise failed! (Use `node --trace-uncaught ...` to show where the exception was thrown)
- Program 2: To resolve the above situation we can make use of reject() method.
javascript
<script>
const p =
new
Promise( ( resolve, reject ) => {
setTimeout( () => {
reject(
'promise failed!'
);
}, 1000);
});
p.
catch
( (err) => {
console.log( err );
});
</script>
- Output: Here the catch block is able to recognise reject() and print the corresponding message.
promise failed!
2. This is a very basic difference. If throw is encountered anywhere inside a function the exception is thrown immediately and the control flow is terminated.In other words after throwing the exception control comes out of the function inside which the exception was thrown.
- Program 1:
javascript
<script>
const p =
new
Promise( ( resolve, reject ) => {
throw
(
'promise failed!'
);
console.log(
"Here"
);
});
p.
catch
( err => {
console.log( err )
});
</script>
- Output: From this example it is clear that the statement console.log(“Here”) is not getting executed.
'promise failed!'
- Program 2: To resolve above situation we use reject() instead of throw the statements after the reject statement inside the function will get executed before the control goes to the catch block.
javascript
<script>
const p =
new
Promise( ( resolve, reject ) => {
reject(
'promise failed!'
);
console.log(
"Here"
);
});
p.
catch
( err => {
console.log( err )
});
</script>
- Output:
Here promise failed!
3. The reject can only be used with a Javascript promise but throw unlike reject can be used to create and throw user-defined exceptions in any try-catch block and not only the ones with promises. If you use Promise.reject() in a try-catch block which is not associated with a promise, UnhandledPromiseRejectionWarning error will pop up.
- Program 1:
javascript
<script>
var
a=20;
try
{
if
( a < 25 )
Promise.reject (
'Less than 25'
);
console.log(
'Okay!'
);
}
catch
(err)
{
console.log(
"inside catch"
);
console.log( err );
}
</script>
- Output: Here, UnhandledPromiseRejectionWarning error comes as Promise.reject() cannot find a catch block associated with a Promise object.
Okay!
- Program 2: The catch block in the above code is not associated with any Promise object and so it is not executed. This is clear from the output as the message “inside catch” is not getting printed. But if we use throw this error will not occur.
javascript
<script>
var
a=20;
try
{
if
( a < 25 )
throw
(
'Less than 25'
);
console.log(
'Okay!'
);
}
catch
(err)
{
console.log(
"inside catch"
);
console.log( err );
}
</script>
- Output:
inside catch Less than 25
Promise
s are important building blocks for asynchronous operations in JavaScript. You may think that promises are not so easy to understand, learn, and work with. And trust me, you are not alone!
Promises are challenging for many web developers, even after spending years working with them.
In this article, I want to try to change that perception while sharing what I’ve learned about JavaScript Promises over the last few years. Hope you find it useful.
What is a Promise in JavaScript?
A Promise
is a special JavaScript object. It produces a value after an asynchronous
(aka, async) operation completes successfully, or an error if it does not complete successfully due to time out, network error, and so on.
Successful call completions are indicated by the resolve
function call, and errors are indicated by the reject
function call.
You can create a promise using the promise constructor like this:
let promise = new Promise(function(resolve, reject) {
// Make an asynchronous call and either resolve or reject
});
In most cases, a promise may be used for an asynchronous operation. However, technically, you can resolve/reject on both synchronous and asynchronous operations.
Hang on, don’t we have callback
functions for async operations?
Oh, yes! That’s right. We have callback
functions in JavaScript. But, a callback is not a special thing in JavaScript. It is a regular function that produces results after an asynchronous
call completes (with success/error).
The word ‘asynchronous’ means that something happens in the future, not right now. Usually, callbacks are only used when doing things like network calls, or uploading/downloading things, talking to databases, and so on.
While callbacks
are helpful, there is a huge downside to them as well. At times, we may have one callback inside another callback that’s in yet another callback and so on. I’m serious! Let’s understand this «callback hell» with an example.
How to Avoid Callback Hell – PizzaHub Example
Let’s order a Veg Margherita pizza 🍕 from the PizzaHub. When we place the order, PizzaHub automatically detects our location, finds a nearby pizza restaurant, and finds if the pizza we are asking for is available.
If it’s available, it detects what kind of beverages we get for free along with the pizza, and finally, it places the order.
If the order is placed successfully, we get a message with a confirmation.
So how do we code this using callback functions? I came up with something like this:
function orderPizza(type, name) {
// Query the pizzahub for a store
query(`/api/pizzahub/`, function(result, error){
if (!error) {
let shopId = result.shopId;
// Get the store and query pizzas
query(`/api/pizzahub/pizza/${shopid}`, function(result, error){
if (!error) {
let pizzas = result.pizzas;
// Find if my pizza is availavle
let myPizza = pizzas.find((pizza) => {
return (pizza.type===type && pizza.name===name);
});
// Check for the free beverages
query(`/api/pizzahub/beverages/${myPizza.id}`, function(result, error){
if (!error) {
let beverage = result.id;
// Prepare an order
query(`/api/order`, {'type': type, 'name': name, 'beverage': beverage}, function(result, error){
if (!error) {
console.log(`Your order of ${type} ${name} with ${beverage} has been placed`);
} else {
console.log(`Bad luck, No Pizza for you today!`);
}
});
}
})
}
});
}
});
}
// Call the orderPizza method
orderPizza('veg', 'margherita');
Let’s have a close look at the orderPizza
function in the above code.
It calls an API to get your nearby pizza shop’s id. After that, it gets the list of pizzas available in that restaurant. It checks if the pizza we are asking for is found and makes another API call to find the beverages for that pizza. Finally the order API places the order.
Here we use a callback for each of the API calls. This leads us to use another callback inside the previous, and so on.
This means we get into something we call (very expressively) Callback Hell
. And who wants that? It also forms a code pyramid which is not only confusing but also error-prone.
There are a few ways to come out of (or not get into) callback hell
. The most common one is by using a Promise
or async
function. However, to understand async
functions well, you need to have a fair understanding of Promise
s first.
So let’s get started and dive into promises.
Understanding Promise States
Just to review, a promise can be created with the constructor syntax, like this:
let promise = new Promise(function(resolve, reject) {
// Code to execute
});
The constructor function takes a function as an argument. This function is called the executor function
.
// Executor function passed to the
// Promise constructor as an argument
function(resolve, reject) {
// Your logic goes here...
}
The executor function takes two arguments, resolve
and reject
. These are the callbacks provided by the JavaScript language. Your logic goes inside the executor function that runs automatically when a new Promise
is created.
For the promise to be effective, the executor function should call either of the callback functions, resolve
or reject
. We will learn more about this in detail in a while.
The new Promise()
constructor returns a promise
object. As the executor function needs to handle async operations, the returned promise object should be capable of informing when the execution has been started, completed (resolved) or retuned with error (rejected).
A promise
object has the following internal properties:
state
– This property can have the following values:
pending
: Initially when the executor function starts the execution.fulfilled
: When the promise is resolved.rejected
: When the promise is rejected.
2. result
– This property can have the following values:
undefined
: Initially when thestate
value ispending
.value
: Whenresolve(value)
is called.error
: Whenreject(error)
is called.
These internal properties are code-inaccessible but they are inspectable. This means that we will be able to inspect the state
and result
property values using the debugger tool, but we will not be able to access them directly using the program.
A promise’s state can be pending
, fulfilled
or rejected
. A promise that is either resolved or rejected is called settled
.
How promises are resolved and rejected
Here is an example of a promise that will be resolved (fulfilled
state) with the value I am done
immediately.
let promise = new Promise(function(resolve, reject) {
resolve("I am done");
});
The promise below will be rejected (rejected
state) with the error message Something is not right!
.
let promise = new Promise(function(resolve, reject) {
reject(new Error('Something is not right!'));
});
An important point to note:
A Promise executor should call only one
resolve
or onereject
. Once one state is changed (pending => fulfilled or pending => rejected), that’s all. Any further calls toresolve
orreject
will be ignored.
let promise = new Promise(function(resolve, reject) {
resolve("I am surely going to get resolved!");
reject(new Error('Will this be ignored?')); // ignored
resolve("Ignored?"); // ignored
});
In the example above, only the first one to resolve will be called and the rest will be ignored.
How to handle a Promise once you’ve created it
A Promise
uses an executor function to complete a task (mostly asynchronously). A consumer function (that uses an outcome of the promise) should get notified when the executor function is done with either resolving (success) or rejecting (error).
The handler methods, .then()
, .catch()
and .finally()
, help to create the link between the executor and the consumer functions so that they can be in sync when a promise resolve
s or reject
s.
How to Use the .then()
Promise Handler
The .then()
method should be called on the promise object to handle a result (resolve) or an error (reject).
It accepts two functions as parameters. Usually, the .then()
method should be called from the consumer function where you would like to know the outcome of a promise’s execution.
promise.then(
(result) => {
console.log(result);
},
(error) => {
console.log(error);
}
);
If you are interested only in successful outcomes, you can just pass one argument to it, like this:
promise.then(
(result) => {
console.log(result);
}
);
If you are interested only in the error outcome, you can pass null
for the first argument, like this:
promise.then(
null,
(error) => {
console.log(error)
}
);
However, you can handle errors in a better way using the .catch()
method that we will see in a minute.
Let’s look at a couple of examples of handling results and errors using the .then
and .catch
handlers. We will make this learning a bit more fun with a few real asynchronous requests. We will use the PokeAPI to get information about Pokémon and resolve/reject them using Promises.
First, let us create a generic function that accepts a PokeAPI URL as argument and returns a Promise. If the API call is successful, a resolved promise is returned. A rejected promise is returned for any kind of errors.
We will be using this function in several examples from now on to get a promise and work on it.
function getPromise(URL) {
let promise = new Promise(function (resolve, reject) {
let req = new XMLHttpRequest();
req.open("GET", URL);
req.onload = function () {
if (req.status == 200) {
resolve(req.response);
} else {
reject("There is an Error!");
}
};
req.send();
});
return promise;
}
Example 1: Get 50 Pokémon’s information:
const ALL_POKEMONS_URL = 'https://pokeapi.co/api/v2/pokemon?limit=50';
// We have discussed this function already!
let promise = getPromise(ALL_POKEMONS_URL);
const consumer = () => {
promise.then(
(result) => {
console.log({result}); // Log the result of 50 Pokemons
},
(error) => {
// As the URL is a valid one, this will not be called.
console.log('We have encountered an Error!'); // Log an error
});
}
consumer();
Example 2: Let’s try an invalid URL
const POKEMONS_BAD_URL = 'https://pokeapi.co/api/v2/pokemon-bad/';
// This will reject as the URL is 404
let promise = getPromise(POKEMONS_BAD_URL);
const consumer = () => {
promise.then(
(result) => {
// The promise didn't resolve. Hence, it will
// not be executed.
console.log({result});
},
(error) => {
// A rejected prmise will execute this
console.log('We have encountered an Error!'); // Log an error
}
);
}
consumer();
How to Use the .catch()
Promise Handler
You can use this handler method to handle errors (rejections) from promises. The syntax of passing null
as the first argument to the .then()
is not a great way to handle errors. So we have .catch()
to do the same job with some neat syntax:
// This will reject as the URL is 404
let promise = getPromise(POKEMONS_BAD_URL);
const consumer = () => {
promise.catch(error => console.log(error));
}
consumer();
If we throw an Error like new Error("Something wrong!")
instead of calling the reject
from the promise executor and handlers, it will still be treated as a rejection. It means that this will be caught by the .catch
handler method.
This is the same for any synchronous exceptions that happen in the promise executor and handler functions.
Here is an example where it will be treated like a reject and the .catch
handler method will be called:
new Promise((resolve, reject) => {
throw new Error("Something is wrong!");// No reject call
}).catch((error) => console.log(error));
How to Use the .finally()
Promise Handler
The .finally()
handler performs cleanups like stopping a loader, closing a live connection, and so on. The finally()
method will be called irrespective of whether a promise resolve
s or reject
s. It passes through the result or error to the next handler which can call a .then() or .catch() again.
Here is an example that’ll help you understand all three methods together:
let loading = true;
loading && console.log('Loading...');
// Gatting Promise
promise = getPromise(ALL_POKEMONS_URL);
promise.finally(() => {
loading = false;
console.log(`Promise Settled and loading is ${loading}`);
}).then((result) => {
console.log({result});
}).catch((error) => {
console.log(error)
});
To explain a bit further:
- The
.finally()
method makes loadingfalse
. - If the promise resolves, the
.then()
method will be called. If the promise rejects with an error, the.catch()
method will be called. The.finally()
will be called irrespective of the resolve or reject.
What is the Promise Chain?
The promise.then()
call always returns a promise. This promise will have the state
as pending
and result
as undefined
. It allows us to call the next .then
method on the new promise.
When the first .then
method returns a value, the next .then
method can receive that. The second one can now pass to the third .then()
and so on. This forms a chain of .then
methods to pass the promises down. This phenomenon is called the Promise Chain
.
Here is an example:
let promise = getPromise(ALL_POKEMONS_URL);
promise.then(result => {
let onePokemon = JSON.parse(result).results[0].url;
return onePokemon;
}).then(onePokemonURL => {
console.log(onePokemonURL);
}).catch(error => {
console.log('In the catch', error);
});
Here we first get a promise resolved and then extract the URL to reach the first Pokémon. We then return that value and it will be passed as a promise to the next .then() handler function. Hence the output,
https://pokeapi.co/api/v2/pokemon/1/
The .then
method can return either:
- A value (we have seen this already)
- A brand new promise.
It can also throw an error.
Here is an example where we have created a promise chain with the .then
methods which returns results and a new promise:
// Promise Chain with multiple then and catch
let promise = getPromise(ALL_POKEMONS_URL);
promise.then(result => {
let onePokemon = JSON.parse(result).results[0].url;
return onePokemon;
}).then(onePokemonURL => {
console.log(onePokemonURL);
return getPromise(onePokemonURL);
}).then(pokemon => {
console.log(JSON.parse(pokemon));
}).catch(error => {
console.log('In the catch', error);
});
In the first .then
call we extract the URL and return it as a value. This URL will be passed to the second .then
call where we are returning a new promise taking that URL as an argument.
This promise will be resolved and passed down to the chain where we get the information about the Pokémon. Here is the output:
In case there is an error or a promise rejection, the .catch method in the chain will be called.
A point to note: Calling .then
multiple times doesn’t form a Promise chain. You may end up doing something like this only to introduce a bug in the code:
let promise = getPromise(ALL_POKEMONS_URL);
promise.then(result => {
let onePokemon = JSON.parse(result).results[0].url;
return onePokemon;
});
promise.then(onePokemonURL => {
console.log(onePokemonURL);
return getPromise(onePokemonURL);
});
promise.then(pokemon => {
console.log(JSON.parse(pokemon));
});
We call the .then
method three times on the same promise, but we don’t pass the promise down. This is different than the promise chain. In the above example, the output will be an error.
How to Handle Multiple Promises
Apart from the handler methods (.then, .catch, and .finally), there are six static methods available in the Promise API. The first four methods accept an array of promises and run them in parallel.
- Promise.all
- Promise.any
- Promise.allSettled
- Promise.race
- Promise.resolve
- Promise.reject
Let’s go through each one.
The Promise.all() method
Promise.all([promises])
accepts a collection (for example, an array) of promises as an argument and executes them in parallel.
This method waits for all the promises to resolve and returns the array of promise results. If any of the promises reject or execute to fail due to an error, all other promise results will be ignored.
Let’s create three promises to get information about three Pokémons.
const BULBASAUR_POKEMONS_URL = 'https://pokeapi.co/api/v2/pokemon/bulbasaur';
const RATICATE_POKEMONS_URL = 'https://pokeapi.co/api/v2/pokemon/raticate';
const KAKUNA_POKEMONS_URL = 'https://pokeapi.co/api/v2/pokemon/kakuna';
let promise_1 = getPromise(BULBASAUR_POKEMONS_URL);
let promise_2 = getPromise(RATICATE_POKEMONS_URL);
let promise_3 = getPromise(KAKUNA_POKEMONS_URL);
Use the Promise.all() method by passing an array of promises.
Promise.all([promise_1, promise_2, promise_3]).then(result => {
console.log({result});
}).catch(error => {
console.log('An Error Occured');
});
Output:
As you see in the output, the result of all the promises is returned. The time to execute all the promises is equal to the max time the promise takes to run.
The Promise.any() method
Promise.any([promises])
— Similar to the all()
method, .any()
also accepts an array of promises to execute them in parallel. This method doesn’t wait for all the promises to resolve. It is done when any one of the promises is settled.
Promise.any([promise_1, promise_2, promise_3]).then(result => {
console.log(JSON.parse(result));
}).catch(error => {
console.log('An Error Occured');
});
The output would be the result of any of the resolved promises:
The Promise.allSettled() method
romise.allSettled([promises])
— This method waits for all promises to settle(resolve/reject) and returns their results as an array of objects. The results will contain a state (fulfilled/rejected) and value, if fulfilled. In case of rejected status, it will return a reason for the error.
Here is an example of all fulfilled promises:
Promise.allSettled([promise_1, promise_2, promise_3]).then(result => {
console.log({result});
}).catch(error => {
console.log('There is an Error!');
});
Output:
If any of the promises rejects, say, the promise_1,
let promise_1 = getPromise(POKEMONS_BAD_URL);
The Promise.race() method
Promise.race([promises])
– It waits for the first (quickest) promise to settle, and returns the result/error accordingly.
Promise.race([promise_1, promise_2, promise_3]).then(result => {
console.log(JSON.parse(result));
}).catch(error => {
console.log('An Error Occured');
});
Output the fastest promise that got resolved:
The Promise.resolve/reject methods
Promise.resolve(value)
– It resolves a promise with the value passed to it. It is the same as the following:
let promise = new Promise(resolve => resolve(value));
Promise.reject(error)
– It rejects a promise with the error passed to it. It is the same as the following:
let promise = new Promise((resolve, reject) => reject(error));
Can we rewrite the PizzaHub example with Promises?
Sure, let’s do it. Let us assume that the query
method will return a promise. Here is an example query() method. In real life, this method may talk to a database and return results. In this case, it is very much hard-coded but serves the same purpose.
function query(endpoint) {
if (endpoint === `/api/pizzahub/`) {
return new Promise((resolve, reject) => {
resolve({'shopId': '123'});
})
} else if (endpoint.indexOf('/api/pizzahub/pizza/') >=0) {
return new Promise((resolve, reject) => {
resolve({pizzas: [{'type': 'veg', 'name': 'margherita', 'id': '123'}]});
})
} else if (endpoint.indexOf('/api/pizzahub/beverages') >=0) {
return new Promise((resolve, reject) => {
resolve({id: '10', 'type': 'veg', 'name': 'margherita', 'beverage': 'coke'});
})
} else if (endpoint === `/api/order`) {
return new Promise((resolve, reject) => {
resolve({'type': 'veg', 'name': 'margherita', 'beverage': 'coke'});
})
}
}
Next is the refactoring of our callback hell
. To do that, first, we will create a few logical functions:
// Returns a shop id
let getShopId = result => result.shopId;
// Returns a promise with pizza list for a shop
let getPizzaList = shopId => {
const url = `/api/pizzahub/pizza/${shopId}`;
return query(url);
}
// Returns a promise with pizza that matches the customer request
let getMyPizza = (result, type, name) => {
let pizzas = result.pizzas;
let myPizza = pizzas.find((pizza) => {
return (pizza.type===type && pizza.name===name);
});
const url = `/api/pizzahub/beverages/${myPizza.id}`;
return query(url);
}
// Returns a promise after Placing the order
let performOrder = result => {
let beverage = result.id;
return query(`/api/order`, {'type': result.type, 'name': result.name, 'beverage': result.beverage});
}
// Confirm the order
let confirmOrder = result => {
console.log(`Your order of ${result.type} ${result.name} with ${result.beverage} has been placed!`);
}
Use these functions to create the required promises. This is where you should compare with the callback hell
example. This is so nice and elegant.
function orderPizza(type, name) {
query(`/api/pizzahub/`)
.then(result => getShopId(result))
.then(shopId => getPizzaList(shopId))
.then(result => getMyPizza(result, type, name))
.then(result => performOrder(result))
.then(result => confirmOrder(result))
.catch(function(error){
console.log(`Bad luck, No Pizza for you today!`);
})
}
Finally, call the orderPizza() method by passing the pizza type and name, like this:
orderPizza('veg', 'margherita');
What’s next from here?
If you are here and have read through most of the lines above, congratulations! You should now have a better grip of JavaScript Promises. All the examples used in this article are in this GitHub repository.
Next, you should learn about the async
function in JavaScript which simplifies things further. The concept of JavaScript promises is best learned by writing small examples and building on top of them.
Irrespective of the framework or library (Angular, React, Vue, and so on) we use, async operations are unavoidable. This means that we have to understand promises to make things work better.
Also, I’m sure you will find the usage of the fetch
method much easier now:
fetch('/api/user.json')
.then(function(response) {
return response.json();
})
.then(function(json) {
console.log(json); // {"name": "tapas", "blog": "freeCodeCamp"}
});
- The
fetch
method returns a promise. So we can call the.then
handler method on it. - The rest is about the promise chain which we learned in this article.
Before we end…
Thank you for reading this far! Let’s connect. You can @ me on Twitter (@tapasadhikary) with comments.
You may also like these other articles:
- JavaScript undefined and null: Let’s talk about it one last time!
- JavaScript: Equality comparison with ==, === and Object.is
- The JavaScript `this` Keyword + 5 Key Binding Rules Explained for JS Beginners
- JavaScript TypeOf – How to Check the Type of a Variable or Object in JS
That’s all for now. See you again with my next article soon. Until then, please take good care of yourself.
Learn to code for free. freeCodeCamp’s open source curriculum has helped more than 40,000 people get jobs as developers. Get started
Syntax
- new Promise( /* executor function: */ function(resolve, reject) { })
- promise.then(onFulfilled[, onRejected])
- promise.catch(onRejected)
- Promise.resolve(resolution)
- Promise.reject(reason)
- Promise.all(iterable)
- Promise.race(iterable)
Promises are part of the ECMAScript 2015 specification and browser support is limited, with 88% of browsers worldwide supporting it as of July 2017. The following table gives an overview of the earliest browser versions that provide support for promises.
Chrome | Edge | Firefox | Internet Explorer | Opera | Opera Mini | Safari | iOS Safari |
---|---|---|---|---|---|---|---|
32 | 12 | 27 | x | 19 | x | 7.1 | 8 |
In environments which do not support them, Promise
can be polyfilled. Third-party libraries may also provide extended functionalities, such as automated «promisification» of callback functions or additional methods like progress
—also known as notify
.
The Promises/A+ standard website provides a list of 1.0 and 1.1 compliant implementations. Promise callbacks based on the A+ standard are always executed asynchronously as microtasks in the event loop.
Promise chaining
The then
method of a promise returns a new promise.
const promise = new Promise(resolve => setTimeout(resolve, 5000));
promise
// 5 seconds later
.then(() => 2)
// returning a value from a then callback will cause
// the new promise to resolve with this value
.then(value => { /* value === 2 */ });
Returning a Promise
from a then
callback will append it to the promise chain.
function wait(millis) {
return new Promise(resolve => setTimeout(resolve, millis));
}
const p = wait(5000).then(() => wait(4000)).then(() => wait(1000));
p.then(() => { /* 10 seconds have passed */ });
A catch
allows a rejected promise to recover, similar to how catch
in a try
/catch
statement works. Any chained then
after a catch
will execute its resolve handler using the value resolved from the catch
.
const p = new Promise(resolve => {throw 'oh no'});
p.catch(() => 'oh yes').then(console.log.bind(console)); // outputs "oh yes"
If there are no catch
or reject
handlers in the middle of the chain, a catch
at the end will capture any rejection in the chain:
p.catch(() => Promise.reject('oh yes'))
.then(console.log.bind(console)) // won't be called
.catch(console.error.bind(console)); // outputs "oh yes"
On certain occasions, you may want to «branch» the execution of the functions. You can do it by returning different promises from a function depending on the condition. Later in the code, you can merge all of these branches into one to call other functions on them and/or to handle all errors in one place.
promise
.then(result => {
if (result.condition) {
return handlerFn1()
.then(handlerFn2);
} else if (result.condition2) {
return handlerFn3()
.then(handlerFn4);
} else {
throw new Error("Invalid result");
}
})
.then(handlerFn5)
.catch(err => {
console.error(err);
});
Thus, the execution order of the functions looks like:
promise --> handlerFn1 -> handlerFn2 --> handlerFn5 ~~> .catch()
| ^
V |
-> handlerFn3 -> handlerFn4 -^
The single catch
will get the error on whichever branch it may occur.
Introduction
A Promise
object represents an operation which has produced or will eventually produce a value. Promises provide a robust way to wrap the (possibly pending) result of asynchronous work, mitigating the problem of deeply nested callbacks (known as «callback hell»).
States and control flow
A promise can be in one of three states:
- pending — The underlying operation has not yet completed, and the promise is pending fulfillment.
- fulfilled — The operation has finished, and the promise is fulfilled with a value. This is analogous to returning a value from a synchronous function.
- rejected — An error has occurred during the operation, and the promise is rejected with a reason. This is analogous to throwing an error in a synchronous function.
A promise is said to be settled (or resolved) when it is either fulfilled or rejected. Once a promise is settled, it becomes immutable, and its state cannot change. The then
and catch
methods of a promise can be used to attach callbacks that execute when it is settled. These callbacks are invoked with the fulfillment value and rejection reason, respectively.
Example
const promise = new Promise((resolve, reject) => {
// Perform some work (possibly asynchronous)
// ...
if (/* Work has successfully finished and produced "value" */) {
resolve(value);
} else {
// Something went wrong because of "reason"
// The reason is traditionally an Error object, although
// this is not required or enforced.
let reason = new Error(message);
reject(reason);
// Throwing an error also rejects the promise.
throw reason;
}
});
The then
and catch
methods can be used to attach fulfillment and rejection callbacks:
promise.then(value => {
// Work has completed successfully,
// promise has been fulfilled with "value"
}).catch(reason => {
// Something went wrong,
// promise has been rejected with "reason"
});
Note: Calling promise.then(...)
and promise.catch(...)
on the same promise might result in an Uncaught exception in Promise
if an error occurs, either while executing the promise or inside one of the callbacks, so the preferred way would be to attach the next listener on the promise returned by the previous then
/ catch
.
Alternatively, both callbacks can be attached in a single call to then
:
promise.then(onFulfilled, onRejected);
Attaching callbacks to a promise that has already been settled will immediately place them in the microtask queue, and they will be invoked «as soon as possible» (i.e. immediately after the currently executing script). It is not necessary to check the state of the promise before attaching callbacks, unlike with many other event-emitting implementations.
Live demo
Delay function call
The setTimeout()
method calls a function or evaluates an expression after a specified number of milliseconds. It is also a trivial way to achieve an asynchronous operation.
In this example calling the wait
function resolves the promise after the time specified as first argument:
function wait(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
wait(5000).then(() => {
console.log('5 seconds have passed...');
});
Waiting for multiple concurrent promises
The Promise.all()
static method accepts an iterable (e.g. an Array
) of promises and returns a new promise, which resolves when all promises in the iterable have resolved, or rejects if at least one of the promises in the iterable have rejected.
// wait "millis" ms, then resolve with "value"
function resolve(value, milliseconds) {
return new Promise(resolve => setTimeout(() => resolve(value), milliseconds));
}
// wait "millis" ms, then reject with "reason"
function reject(reason, milliseconds) {
return new Promise((_, reject) => setTimeout(() => reject(reason), milliseconds));
}
Promise.all([
resolve(1, 5000),
resolve(2, 6000),
resolve(3, 7000)
]).then(values => console.log(values)); // outputs "[1, 2, 3]" after 7 seconds.
Promise.all([
resolve(1, 5000),
reject('Error!', 6000),
resolve(2, 7000)
]).then(values => console.log(values)) // does not output anything
.catch(reason => console.log(reason)); // outputs "Error!" after 6 seconds.
Non-promise values in the iterable are «promisified».
Promise.all([
resolve(1, 5000),
resolve(2, 6000),
{ hello: 3 }
])
.then(values => console.log(values)); // outputs "[1, 2, { hello: 3 }]" after 6 seconds
Destructuring assignment can help to retrieve results from multiple promises.
Promise.all([
resolve(1, 5000),
resolve(2, 6000),
resolve(3, 7000)
])
.then(([result1, result2, result3]) => {
console.log(result1);
console.log(result2);
console.log(result3);
});
Waiting for the first of multiple concurrent promises
The Promise.race()
static method accepts an iterable of Promises and returns a new Promise which resolves or rejects as soon as the first of the promises in the iterable has resolved or rejected.
// wait "milliseconds" milliseconds, then resolve with "value"
function resolve(value, milliseconds) {
return new Promise(resolve => setTimeout(() => resolve(value), milliseconds));
}
// wait "milliseconds" milliseconds, then reject with "reason"
function reject(reason, milliseconds) {
return new Promise((_, reject) => setTimeout(() => reject(reason), milliseconds));
}
Promise.race([
resolve(1, 5000),
resolve(2, 3000),
resolve(3, 1000)
])
.then(value => console.log(value)); // outputs "3" after 1 second.
Promise.race([
reject(new Error('bad things!'), 1000),
resolve(2, 2000)
])
.then(value => console.log(value)) // does not output anything
.catch(error => console.log(error.message)); // outputs "bad things!" after 1 second
«Promisifying» values
The Promise.resolve
static method can be used to wrap values into promises.
let resolved = Promise.resolve(2);
resolved.then(value => {
// immediately invoked
// value === 2
});
If value
is already a promise, Promise.resolve
simply recasts it.
let one = new Promise(resolve => setTimeout(() => resolve(2), 1000));
let two = Promise.resolve(one);
two.then(value => {
// 1 second has passed
// value === 2
});
In fact, value
can be any «thenable» (object defining a then
method that works sufficiently like a spec-compliant promise). This allows Promise.resolve
to convert untrusted 3rd-party objects into trusted 1st-party Promises.
let resolved = Promise.resolve({
then(onResolved) {
onResolved(2);
}
});
resolved.then(value => {
// immediately invoked
// value === 2
});
The Promise.reject
static method returns a promise which immediately rejects with the given reason
.
let rejected = Promise.reject("Oops!");
rejected.catch(reason => {
// immediately invoked
// reason === "Oops!"
});
«Promisifying» functions with callbacks
Given a function that accepts a Node-style callback,
fooFn(options, function callback(err, result) { ... });
you can promisify it (convert it to a promise-based function) like this:
function promiseFooFn(options) {
return new Promise((resolve, reject) =>
fooFn(options, (err, result) =>
// If there's an error, reject; otherwise resolve
err ? reject(err) : resolve(result)
)
);
}
This function can then be used as follows:
promiseFooFn(options).then(result => {
// success!
}).catch(err => {
// error!
});
In a more generic way, here’s how to promisify any given callback-style function:
function promisify(func) {
return function(...args) {
return new Promise((resolve, reject) => {
func(...args, (err, result) => err ? reject(err) : resolve(result));
});
}
}
This can be used like this:
const fs = require('fs');
const promisedStat = promisify(fs.stat.bind(fs));
promisedStat('/foo/bar')
.then(stat => console.log('STATE', stat))
.catch(err => console.log('ERROR', err));
Error Handling
Errors thrown from promises are handled by the second parameter (reject
) passed to then
or by the handler passed to catch
:
throwErrorAsync()
.then(null, error => { /* handle error here */ });
// or
throwErrorAsync()
.catch(error => { /* handle error here */ });
Chaining
If you have a promise chain then an error will cause resolve
handlers to be skipped:
throwErrorAsync()
.then(() => { /* never called */ })
.catch(error => { /* handle error here */ });
The same applies to your then
functions. If a resolve
handler throws an exception then the next reject
handler will be invoked:
doSomethingAsync()
.then(result => { throwErrorSync(); })
.then(() => { /* never called */ })
.catch(error => { /* handle error from throwErrorSync() */ });
An error handler returns a new promise, allowing you to continue a promise chain. The promise returned by the error handler is resolved with the value returned by the handler:
throwErrorAsync()
.catch(error => { /* handle error here */; return result; })
.then(result => { /* handle result here */ });
You can let an error cascade down a promise chain by re-throwing the error:
throwErrorAsync()
.catch(error => {
/* handle error from throwErrorAsync() */
throw error;
})
.then(() => { /* will not be called if there's an error */ })
.catch(error => { /* will get called with the same error */ });
It is possible to throw an exception that is not handled by the promise by wrapping the throw
statement inside a setTimeout
callback:
new Promise((resolve, reject) => {
setTimeout(() => { throw new Error(); });
});
This works because promises cannot handle exceptions thrown asynchronously.
Unhandled rejections
An error will be silently ignored if a promise doesn’t have a catch
block or reject
handler:
throwErrorAsync()
.then(() => { /* will not be called */ });
// error silently ignored
To prevent this, always use a catch
block:
throwErrorAsync()
.then(() => { /* will not be called */ })
.catch(error => { /* handle error*/ });
// or
throwErrorAsync()
.then(() => { /* will not be called */ }, error => { /* handle error*/ });
Alternatively, subscribe to the unhandledrejection
event to catch any unhandled rejected promises:
window.addEventListener('unhandledrejection', event => {});
Some promises can handle their rejection later than their creation time. The rejectionhandled
event gets fired whenever such a promise is handled:
window.addEventListener('unhandledrejection', event => console.log('unhandled'));
window.addEventListener('rejectionhandled', event => console.log('handled'));
var p = Promise.reject('test');
setTimeout(() => p.catch(console.log), 1000);
// Will print 'unhandled', and after one second 'test' and 'handled'
The event
argument contains information about the rejection. event.reason
is the error object and event.promise
is the promise object that caused the event.
In Nodejs the rejectionhandled
and unhandledrejection
events are called rejectionHandled
and unhandledRejection
on process
, respectively, and have a different signature:
process.on('rejectionHandled', (reason, promise) => {});
process.on('unhandledRejection', (reason, promise) => {});
The reason
argument is the error object and the promise
argument is a reference to the promise object that caused the event to fire.
Usage of these unhandledrejection
and rejectionhandled
events should be considered for debugging purposes only. Typically, all promises should handle their rejections.
Note: Currently, only Chrome 49+ and Node.js support unhandledrejection
and rejectionhandled
events.
Caveats
Chaining with fulfill
and reject
The then(fulfill, reject)
function (with both parameters not null
) has unique and complex behavior, and shouldn’t be used unless you know exactly how it works.
The function works as expected if given null
for one of the inputs:
// the following calls are equivalent
promise.then(fulfill, null)
promise.then(fulfill)
// the following calls are also equivalent
promise.then(null, reject)
promise.catch(reject)
However, it adopts unique behavior when both inputs are given:
// the following calls are not equivalent!
promise.then(fulfill, reject)
promise.then(fulfill).catch(reject)
// the following calls are not equivalent!
promise.then(fulfill, reject)
promise.catch(reject).then(fulfill)
The then(fulfill, reject)
function looks like it is a shortcut for then(fulfill).catch(reject)
, but it is not, and will cause problems if used interchangeably. One such problem is that the reject
handler does not handle errors from the fulfill
handler. Here is what will happen:
Promise.resolve() // previous promise is fulfilled
.then(() => { throw new Error(); }, // error in the fulfill handler
error => { /* this is not called! */ });
The above code will result in a rejected promise because the error is propagated. Compare it to the following code, which results in a fulfilled promise:
Promise.resolve() // previous promise is fulfilled
.then(() => { throw new Error(); }) // error in the fulfill handler
.catch(error => { /* handle error */ });
A similar problem exists when using then(fulfill, reject)
interchangeably with catch(reject).then(fulfill)
, except with propagating fulfilled promises instead of rejected promises.
Synchronously throwing from function that should return a promise
Imagine a function like this:
function foo(arg) {
if (arg === 'unexepectedValue') {
throw new Error('UnexpectedValue')
}
return new Promise(resolve =>
setTimeout(() => resolve(arg), 1000)
)
}
If such function is used in the middle of a promise chain, then apparently there is no problem:
makeSomethingAsync().
.then(() => foo('unexpectedValue'))
.catch(err => console.log(err)) // <-- Error: UnexpectedValue will be caught here
However, if the same function is called outside of a promise chain, then the error will not be handled by it and will be thrown to the application:
foo('unexpectedValue') // <-- error will be thrown, so the application will crash
.then(makeSomethingAsync) // <-- will not run
.catch(err => console.log(err)) // <-- will not catch
There are 2 possible workarounds:
Return a rejected promise with the error
Instead of throwing, do as follows:
function foo(arg) {
if (arg === 'unexepectedValue') {
return Promise.reject(new Error('UnexpectedValue'))
}
return new Promise(resolve =>
setTimeout(() => resolve(arg), 1000)
)
}
Wrap your function into a promise chain
Your throw
statement will be properly caught when it is already inside a promise chain:
function foo(arg) {
return Promise.resolve()
.then(() => {
if (arg === 'unexepectedValue') {
throw new Error('UnexpectedValue')
}
return new Promise(resolve =>
setTimeout(() => resolve(arg), 1000)
)
})
}
Reconciling synchronous and asynchronous operations
In some cases you may want to wrap a synchronous operation inside a promise to prevent repetition in code branches. Take this example:
if (result) { // if we already have a result
processResult(result); // process it
} else {
fetchResult().then(processResult);
}
The synchronous and asynchronous branches of the above code can be reconciled by redundantly wrapping the synchronous operation inside a promise:
var fetch = result
? Promise.resolve(result)
: fetchResult();
fetch.then(processResult);
When caching the result of an asynchronous call, it is preferable to cache the promise rather than the result
itself. This ensures that only one asynchronous operation is required to resolve multiple parallel requests.
Care should be taken to invalidate cached values when error conditions are encountered.
// A resource that is not expected to change frequently
var planets = 'http://swapi.co/api/planets/';
// The cached promise, or null
var cachedPromise;
function fetchResult() {
if (!cachedPromise) {
cachedPromise = fetch(planets)
.catch(function (e) {
// Invalidate the current result to retry on the next fetch
cachedPromise = null;
// re-raise the error to propagate it to callers
throw e;
});
}
return cachedPromise;
}
Reduce an array to chained promises
This design pattern is useful for generating a sequence of asynchronous actions from a list of elements.
There are two variants :
- the «then» reduction, which builds a chain that continues as long as the chain experiences success.
- the «catch» reduction, which builds a chain that continues as long as the chain experiences error.
The «then» reduction
This variant of the pattern builds a .then()
chain, and might be used for chaining animations, or making a sequence of dependent HTTP requests.
[1, 3, 5, 7, 9].reduce((seq, n) => {
return seq.then(() => {
console.log(n);
return new Promise(res => setTimeout(res, 1000));
});
}, Promise.resolve()).then(
() => console.log('done'),
(e) => console.log(e)
);
// will log 1, 3, 5, 7, 9, 'done' in 1s intervals
Explanation:
- We call
.reduce()
on a source array, and providePromise.resolve()
as an initial value. - Every element reduced will add a
.then()
to the initial value. reduce()
‘s product will be Promise.resolve().then(…).then(…).- We manually append a
.then(successHandler, errorHandler)
after the reduce, to executesuccessHandler
once all the previous steps have resolved. If any step was to fail, thenerrorHandler
would execute.
Note: The «then» reduction is a sequential counterpart of Promise.all()
.
The «catch» reduction
This variant of the pattern builds a .catch()
chain and might be used for sequentially probing a set of web servers for some mirrored resource until a working server is found.
var working_resource = 5; // one of the values from the source array
[1, 3, 5, 7, 9].reduce((seq, n) => {
return seq.catch(() => {
console.log(n);
if(n === working_resource) { // 5 is working
return new Promise((resolve, reject) => setTimeout(() => resolve(n), 1000));
} else { // all other values are not working
return new Promise((resolve, reject) => setTimeout(reject, 1000));
}
});
}, Promise.reject()).then(
(n) => console.log('success at: ' + n),
() => console.log('total failure')
);
// will log 1, 3, 5, 'success at 5' at 1s intervals
Explanation:
- We call
.reduce()
on a source array, and providePromise.reject()
as an initial value. - Every element reduced will add a
.catch()
to the initial value. reduce()
‘s product will bePromise.reject().catch(...).catch(...)
.- We manually append
.then(successHandler, errorHandler)
after the reduce, to executesuccessHandler
once any of the previous steps has resolved. If all steps were to fail, thenerrorHandler
would execute.
Note: The «catch» reduction is a sequential counterpart of Promise.any()
(as implemented in bluebird.js
, but not currently in native ECMAScript).
forEach with promises
It is possible to effectively apply a function (cb
) which returns a promise to each element of an array, with each element waiting to be processed until the previous element is processed.
function promiseForEach(arr, cb) {
var i = 0;
var nextPromise = function () {
if (i >= arr.length) {
// Processing finished.
return;
}
// Process next function. Wrap in `Promise.resolve` in case
// the function does not return a promise
var newPromise = Promise.resolve(cb(arr[i], i));
i++;
// Chain to finish processing.
return newPromise.then(nextPromise);
};
// Kick off the chain.
return Promise.resolve().then(nextPromise);
};
This can be helpful if you need to efficiently process thousands of items, one at a time. Using a regular for
loop to create the promises will create them all at once and take up a significant amount of RAM.
Performing cleanup with finally()
There is currently a proposal (not yet part of the ECMAScript standard) to add a finally
callback to promises that will be executed regardless of whether the promise is fulfilled or rejected. Semantically, this is similar to the finally
clause of the try
block.
You would usually use this functionality for cleanup:
var loadingData = true;
fetch('/data')
.then(result => processData(result.data))
.catch(error => console.error(error))
.finally(() => {
loadingData = false;
});
It is important to note that the finally
callback doesn’t affect the state of the promise. It doesn’t matter what value it returns, the promise stays in the fulfilled/rejected state that it had before. So in the example above the promise
will be resolved with the return value of processData(result.data)
even though the finally
callback returned undefined
.
With the standardization process still being in progress, your promises implementation most likely won’t support finally
callbacks out of the box. For synchronous callbacks you can add this functionality with a polyfill however:
if (!Promise.prototype.finally) {
Promise.prototype.finally = function(callback) {
return this.then(result => {
callback();
return result;
}, error => {
callback();
throw error;
});
};
}
Asynchronous API request
This is an example of a simple GET
API call wrapped in a promise to take advantage of its asynchronous functionality.
var get = function(path) {
return new Promise(function(resolve, reject) {
let request = new XMLHttpRequest();
request.open('GET', path);
request.onload = resolve;
request.onerror = reject;
request.send();
});
};
More robust error handling can be done using the following onload
and onerror
functions.
request.onload = function() {
if (this.status >= 200 && this.status < 300) {
if(request.response) {
// Assuming a successful call returns JSON
resolve(JSON.parse(request.response));
} else {
resolve();
} else {
reject({
'status': this.status,
'message': request.statusText
});
}
};
request.onerror = function() {
reject({
'status': this.status,
'message': request.statusText
});
};
Using ES2017 async/await
The same example above, Image loading, can be written using async functions. This also allows using the common try/catch
method for exception handling.
Note: as of April 2017, the current releases of all browsers but Internet Explorer supports async functions.
function loadImage(url) {
return new Promise((resolve, reject) => {
const img = new Image();
img.addEventListener('load', () => resolve(img));
img.addEventListener('error', () => {
reject(new Error(`Failed to load ${url}`));
});
img.src = url;
});
}
(async () => {
// load /image.png and append to #image-holder, otherwise throw error
try {
let img = await loadImage('http://example.com/image.png');
document.getElementById('image-holder').appendChild(img);
}
catch (error) {
console.error(error);
}
})();
JavaScript promises allow asynchronous code to use structured error handling. The promise chains are served as a great way of error handling.
Whenever a promise rejects, the control jumps to the nearest rejection handler.
One of the most useful methods of error-handling is .catch.
As it was already mentioned, .catch is one of the most useful methods for error handling in JavaScript.
Let’s view a case, in which the URL to fetch is wrong (no such site), and the .catch method handles it:
Javascript promise fetch is wrong and .catch method
fetch(‘https://noSuchServer.someText’) // rejects
.then(response => response.json())
.catch(err => console.log(err)) // TypeError: failed to fetch (the text may vary)
But note that .catch is not immediate. It might appear after one or several .then.
Everything might be correct with the site, but the response is not valid JSON.
The simplest way of catching all the errors is to append .catch to the end of the chain. Here is an example:
Javascript promise fetch is wrong and .catch method
fetch(‘/promiseChaining/user.json’)
.then(response => response.json())
.then(user => fetch(`https://api.github.com/users/${user.name}`))
.then(response => response.json())
.then(user => new Promise((resolve, reject) => {
let img = document.createElement(‘img’);
img.src = user.avatarUrl;
img.className = «promiseAvatarExample»;
document.body.append(img);
setTimeout(() => {
img.remove();
resolve(user);
}, 3000);
}))
.catch(error => console.log(error.message));
Usually, .catch doesn’t trigger. But, in case any of the promises, as mentioned earlier, rejects, it will catch it.
There is an invisible try..catch around the code of a promise handler and promise executor. In case of an exception, it will be treated as a rejection.
It is shown in the following code:
new Promise((resolve, reject) => {
throw new Error("Error!!");
}).catch(console.log); // Error: Error!!
It operates the same as this code:
Javascript try..catch
new Promise((resolve, reject) => {
reject(new Error(«Error!!»));
}).catch(console.log); // Error: Error!!
The invisible try..catch will catch the error and transform it into a rejected promise. It can happen both in the executor function and its handlers. In case you throw inside a .then handler, it means a rejected promise, and the control jumps to the closest error handler.
An example will look like this:
Javascript try..catch
new Promise((resolve, reject) => {
resolve(«Yes»);
}).then((result) => {
throw new Error(«Error!!»); // rejects the promise
}).catch(console.log); // Error: Error!!
That may happen to all the errors, not just ones caused by the throw statement.
We can consider a programming error as an example:
Javascript throw statement
new Promise((resolve, reject) => {
resolve(«Yes»);
}).then((result) => {
someFunction(); // no such function
}).catch(console.log); // ReferenceError: someFunction is not defined
The final .catch will catch both explicit rejections and accidental errors in the handlers.
As it was already stated, .catch at the end of the promise chain is equivalent to try..catch. You may have as many .then handlers as you like, then use a single .catch at the end of the chain for handling all the errors.
The regulartry..catch allows you to analyze an error and rethrow it if it can’t be handled. A similar situation is possible for promises.
If you throw inside .catch, then the control will go to the next nearest error handler. If you handle the error and finish normally, it will continue to the next nearest successful .then handler.
The example below illustrates the successful error-handling with .catch:
Javascript error-handling with .catch
// the execution: catch -> then
new Promise((resolve, reject) => {
throw new Error(«Error!!»);
}).catch(function (error) {
console.log(«The error-handling, continue normally»);
}).then(() => console.log(«The next successful handler runs»));
In the example above, the .catch block finishes normally. Hence, the next effective .then is called.
Now let’s check out another situation with .catch. The handler (*) catches the error but is not capable of handling it:
Javascript error-handling with .catch console.log
// the execution: catch -> catch -> then
new Promise((resolve, reject) => {
throw new Error(«Error!!»);
}).catch(function (error) { // (*)
if (error instanceof URIError) {
// handle
} else {
console.log(«Can’t handle such a error»);
throw error; // throwing this or that error jumps to the next catch
}
}).then(function () {
/* doesn’t run here */
}).catch(error => { // (**)
console.log(`The unknown error: ${error}`);
// do not return anything => execution goes the usual way
});
The execution jumps from the initial .catch (*) to the following one down the chain.
In this section, we will examine the cases when errors are not handled.
Let’s see that you have forgotten to append .catch to the end of the chain.
Here is an example:
new Promise(function () {
noSuchFunc(); // Error here, no such function
})
.then(() => {
// successful promise handlers
}); // no append .catch at the end
If there is an error, the promise will be rejected. The execution will jump to the nearest rejection handler. But there exists none, and the error will get “stuck”.There isn’t any code for handling it.
So, what will happen if an error is not caught by try..catch? The script will collapse with a console message. Things like that occur with unhandled promise rejections.
The engine of JavaScript usually tracks this kind of rejections, generating a global error.
For catching such errors in the browser, you can use the event unhandledRejection, as follows:
window.addEventListener('unhandledRejection', function (event) {
// the event object has two special properties
console.log(event.promise); // [object Promise] - error
console.log(event.reason); // Error: Error!! - the unhandled error
});
new Promise(function () {
throw new Error("Error!!");
}); // no catch to handle the error
So, in case there is an error, and no .catch can be found, the unhandledRejection will trigger getting the event object with the information regarding the error.
As a rule, this kind of errors are unrecoverable. The most proper solution in such circumstances is to inform the user about it, reporting the incident to the server.
Non-browser environments, such as Node.js, include other options for tracking unhandled errors.
One of the most significant assets of using promises is the way they allow you to handle errors.
Errors in the promises can be handled with .catch: no matter it’s a reject() call or an error thrown in a handler. It would be best if you put .catch precisely in the places where you want to handle errors. The handler analyzes the errors rethrowing the ones that are unknown (for example, programming mistakes).
In any other case, you need to have unhandledRejection event handler ( for browsers and analogs of different environments). It will track unhandled errors informing the user about them. It will help you avoid the collapse of your app.