Since 2015, JavaScript has improved immensely.
It’s much more pleasant to use it now than ever.
In this article, we’ll look at handling JavaScript promise exceptions.
Handling Exceptions in Promise-Based Functions
We can handle exceptions in promise-based functions.
If exceptions are thrown in then
and catch
callbacks, then the methods convert them into rejections.
However, if we run the synchronous code before the promise code, then the whole function throws an exception.
This may be a problem that we want to solve.
One way to solve this is to surround the synchronous and promise code with try-catch.
For example, we can write:
function foo() {
try {
syncFn();
return asyncFn()
.then(result => {
//...
});
} catch (err) {
return Promise.reject(err);
}
}
We have the foo
function with the try-catch block which runs syncFn
first and then asyncFn
which returns a promise.
We return the promise chain in the try block.
And if the catch
block is run, then we return a rejected promise.
This lets us keep the function async all the way through.
Run the Synchronous Code in the Callback
We can also run the synchronous code in the callback.
For instance, we can write:
function foo() {
return asyncFn()
.then(result => {
syncFn();
return asyncFn2();
})
.then(result => {
//...
});
}
If the synchronous syncFn
functions run in the then
callback and it throws an exception, then the returned promise will be rejected.
The start of the promise chain can also be in the Promise
constructor.
For example, we can write:
function foo() {
return new Promise((resolve, reject) => {
syncFn();
resolve(asyncFn());
})
.then(result => {
//...
});
}
We run syncFn
in the Promise
constructor callback so that we run the callback so that exceptions will be propagated if an error is thrown by syncFn
.
Composing Promises
We can run multiple promises in various ways.
If we want to run multiple unrelated promises in parallel, we can use the Promise.all
method.
For example, we can write:
Promise.all([
asyncFn1(),
asyncFn2(),
])
.then(([result1, result2]) => {
//...
})
.catch(err => {
//...
});
We pass in an array of promise-returning functions.
Then we pass that in Promise.all
.
And then we call then
with a callback that has an array of the resolved values of both values.
Then we call catch
to catch the errors occurring with them.
We can loop through the resolved items with them.
For example, we can write:
Promise.all([
asyncFn1(),
asyncFn2(),
])
.then((results) => {
for (const result of results) {
console.log(result);
}
})
.catch(err => {
//...
});
We get the results
array and looped through it with the for-of loop.
Promise.race()
Promise.race
is another method to chain promises.
It takes an array of promises and returns a promise that’s resolved to the value of the promise that’s resolved first.
For example, we can write:
Promise.race([
asyncFn(),
delay(3000).then(function() {
throw new Error('timed out')
})
])
.then(function(text) {
//...
})
.catch(function(reason) {
//...
});
We have an array of promises with the asyncFn
and delay
which both return promises.
Then we call then
with a callback to get the resolved value from the promise that’s resolved the earliest.
Conclusion
We can handle errors with promise-based functions in a few ways.
Also, we can run multiple promises in many different ways.