Bootcamp
Search…
0.8.1: Promise.prototype.catch, Promise.all

Introduction

This module introduces more advanced usage of Promises via the .catch and .all methods.

.catch

We had previously seen .catch in Module 3.7: JavaScript Promises but may not have used it in our apps. Let's discuss what .catch does more specifically and how to use it. We'll use Promises returned by pg library functions as examples but any Promise (with or without external libraries) will have the same .catch behaviour.

Catch Errors from Chained Promises

On line 12 we throw an exception to exit this series of dependent queries because without a valid recipe we cannot proceed. The .catch block on line 19 catches this exception and handles it gracefully.
let category = 'vegan';
​
client
.query('SELECT * from categories WHERE name= $1', [category])
.then((result) => {
let categoryId = result.rows[0].id;
return pool.query('SELECT * FROM recipes WHERE category_id = $1', [
categoryId,
]);
})
.then((result) => {
// check to see if we got results
if (result.rows.length === 0) {
throw 'no recipes';
}
​
let recipes = result.rows;
​
// ...
})
.catch((error) => {
// in Express.js we might respond 400 or 500.
console.log(error);
});

Catch Errors from Individual Promise

In the following example we see that a throw in a self-constructed Promise can also be caught by a .catch block on that Promise. On line 25 we declare the catch block that will handle the throw on line 11.
// random number representing between 0 and .999 seconds
const randomNumber = () => Math.floor(Math.random() * 1000);
​
// function that wraps setTimeout in a promise
const setDelay = (delay) => {
console.log(`delaying ${delay}`);
​
return new Promise((resolve, reject) => {
// throw if we have to wait too long
if (delay > 500) {
throw "we'll wait too long";
}
​
setTimeout(() => {
// timeout is over, resolve
resolve();
}, delay);
});
};
​
setDelay(randomNumber())
.then(() => {
console.log('all done with delay');
})
.catch((error) => {
console.log('catching error');
console.log(error);
});

.all

Sometimes we may wish to wait for a set of Promises to resolve that do not need to execute sequentially. With .then syntax, we are forced to wait for these Promises to resolve sequentially, which can slow down our program. With .all, we can allow the Promises to execute in parallel, and only run the following code in program after all specified Promises have resolved.

Resolve Unrelated DB Query Promises in Parallel

const results = Promise.all([
pool.query('SELECT * FROM recipes'),
pool.query('SELECT * FROM users'),
pool.query('SELECT * FROM categories'),
// allResults is an array of results whose elements correspond
// to the elements in the Promise.all parameter array
]).then((allResults) => {
console.log(allResults);
});

Comparison of Sequential vs Parallel Promise Resolution

The following example is a simulation of sequential vs parallel execution time.

Sequential Promise Resolution

The delay will be the sum of all the delays.
const randomNumber = () => Math.floor(Math.random() * 1000);
​
const setDelay = (delay) => {
console.log(`delaying ${delay}`);
​
return new Promise((resolve, reject) => {
setTimeout(() => resolve(delay), delay);
});
};
​
// do nested setTimeouts, one after the other
const result = setDelay(randomNumber()).then((resultOne) => {
return setDelay(randomNumber()).then((resultTwo) => {
return setDelay(randomNumber()).then((resultThree) => {
return setDelay(randomNumber());
});
});
});
​
// run this when they are all done
result.then((lastDelay) => {
console.log('all done');
console.log(lastDelay);
});

Parallel Promise Resolution with Promise.all

The delay will be as long as the longest delay.
const randomNumber = () => Math.floor(Math.random() * 1000);
​
const setDelay = (delay) => {
console.log(`delaying: ${delay}`);
​
return new Promise((resolve, reject) => {
setTimeout(() => resolve(delay), delay);
});
};
​
// start all the setTimeouts
// results is a Promise that resolves to an array of values that
// correspond to the resolve values of each element in the Promise.all
// parameter array.
const results = Promise.all([
setDelay(randomNumber()),
setDelay(randomNumber()),
setDelay(randomNumber()),
setDelay(randomNumber()),
]);
​
results.then((arrayOfResolvedValues) => {
console.log('all done');
console.log(arrayOfResolvedValues);
});

Exercise

Run the Comparison of Sequential vs Parallel Promise Resolution code examples above and compare sequential vs parallel processing time.