📖 Deeper dive reading:
The rendering process of your HTML executes on a single thread. That means that you cannot take a long time processing JavaScript on the main rendering thread. Long running, or blocking tasks, should be executed with the use of a JavaScript Promise. The execution of a promise allows the main rendering thread to continue while some action is executed in the background.
You create a promise by calling the Promise object constructor and passing it an executor function that runs the asynchronous operation. Executing asynchronously means that promise constructor may return before the promise executor function runs. The state of the promise execution is always in one of three possible states.
- pending - Currently running asynchronously
- fulfilled - Completed successfully
- rejected - Failed to complete
We can demonstrate asynchronous execution by using the standard JavaScript setTimeout function to create a delay in the execution of the code. The setTimeout function takes the number of milliseconds to wait and a function to call after that amount of time has expired. We call the delay function in a for loop in the promise executor and also in a for loop outside the promise so that both code blocks are running in parallel.
const delay = (msg, wait) => {
setTimeout(() => {
console.log(msg, wait);
}, 1000 * wait);
};
new Promise((resolve, reject) => {
// Code executing in the promise
for (let i = 0; i < 3; i++) {
delay('In promise', i);
}
});
// Code executing after the promise
for (let i = 0; i < 3; i++) {
delay('After promise', i);
}
// OUTPUT:
// In promise 0
// After promise 0
// In promise 1
// After promise 1
// In promise 2
// After promise 2Now that we know how to use a promise to execute asynchronously, we need to be able to set the state to fulfilled when things complete correctly, or to rejected when an error happens. The promise executor function takes two functions as parameters, resolve and reject. Calling resolve sets the promise to the fulfilled state, and calling reject sets the promise to the rejected state.
Consider the following "coin toss" promise that waits ten seconds and then has a fifty percent chance of resolving or rejecting.
const coinToss = new Promise((resolve, reject) => {
setTimeout(() => {
if (Math.random() > 0.5) {
resolve('success');
} else {
reject('error');
}
}, 10000);
});If you log the coinToss promise object to the console immediately after calling the constructor, it will display that it is in the pending state.
console.log(coinToss);
// OUTPUT: Promise {<pending>}If you wait ten seconds and then log the coinToss promise object again, the state will either show as fulfilled or rejected depending upon the way the coin landed.
console.log(coinToss);
// OUTPUT: Promise {<fulfilled>}With the ability to asynchronously execute and set the resulting state, we now need a way to generically do something with the result of a promise after it resolves. This is done with functionality similar to exception handling. The promise object has three functions: then, catch, and finally. The then function is called if the promise is fulfilled, catch is called if the promise is rejected, and finally is always called after all the processing is completed.
We can rework our coinToss example and make it so 10 percent of the time the coin falls off the table and resolves to the rejected state. Otherwise the promise resolves to fulfilled with a result of either heads or tails.
const coinToss = new Promise((resolve, reject) => {
setTimeout(() => {
if (Math.random() > 0.1) {
resolve(Math.random() > 0.5 ? 'heads' : 'tails');
} else {
reject('fell off table');
}
}, 10000);
});We then chain the then, catch and finally functions to the coinToss object in order to handle each of the possible results.
coinToss
.then((result) => console.log(`Coin toss result: ${result}`))
.catch((err) => console.log(`Error: ${err}`))
.finally(() => console.log('Toss completed'));
// OUTPUT:
// Coin toss result: tails
// Toss completedThis CodePen uses promises to order pizzas. Create a fork of the pen and take some time to experiment with it. Modify the CodePen to include a new function that makes the pizza and include it in the promise chain.
If your section of this course requires that you submit assignments for grading: Submit your CodePen URL to the Canvas assignment.
If you get stuck here is a possible solution.