} };
navbar.show(user);
account.show(user);
// Console output:
// *** Navbar ***
// Name: Samantha // *** Account Info ***
// Name: Samantha
// Send lots of email? true
Here a user object with a profilePromise property and a getProfile method is cre‐
ated. The getProfile method returns a promise that is resolved with an object con‐
taining the user profile information. Then the script passes the user to the navbar and account objects, which display information from the profile.
Remember that a promise serves as a placeholder for the result of an operation. In this case, the user.profilePromise is a placeholder used by the navbar.show() and account.show() functions. These functions can be safely called anytime before or after the profile data is available. The callbacks they use to print the data to the con‐
sole will only be invoked once the profile is loaded. This removes the need for an if statement in either function to check whether the data is ready.
In addition to that simplification, using the promise placeholder has another benefit.
It removes the need for signaling inside the getProfile function to display the user‐
name and profile once the data is ready. The promise implicitly provides that logic, happily decoupled from the details of how or when the data is displayed.
Promise States
The state of an operation represented by a promise is stored within the promise. At any given moment an operation has either not begun, is in progress, has run to com‐
pletion, or has stopped and cannot be completed. These conditions are represented by three mutually exclusive states:
Pending
The operation has not begun or is in progress.
Fulfilled
The operation has completed.
Promise States | 15
Rejected
The operation could not be completed.
Figure 2-1 shows the relationship between the three states.
Figure 2-1. Promise states
In the examples so far, we refer to the fulfilled and rejected states as success and error, respectively. There is a difference between these terms. An operation could complete with an error (although that may be bad form) and an operation may not complete because it was cancelled even though no error occurred. Hence, the terms fulfilled and rejected are better descriptions for these states than success and error.
When a promise is no longer pending it is said to be settled. This is a general term indicating the promise has reached its final state. Once a pending promise is settled the transition is permanent. Both the state and any value given as the result cannot be changed from that point on. This behavior is consistent with how operations work in real life. A completed operation cannot become incomplete and its result does not change. Of course a program may repeat the steps of an operation multiple times. For instance, a failed operation may be retried and multiple tries may return different val‐
ues. In that case, a new promise represents each try, so a more descriptive way to think of a promise is a placeholder for the result of one attempt of an operation.
The code in Example 2-6 demonstrates how the state of a promise can only be changed once. The code calls resolve and reject in the same promise constructor.
The call to resolve changes the state of the promise from pending to fulfilled. Any further calls to resolve or reject are ignored because the promise is already fulfilled.
Example 2-6. The state of a promise never changes after it is fulfilled or rejected
var promise = new Promise(function (resolve, reject) { resolve(Math.PI);
reject(0); // Does nothing resolve(Math.sqrt(-1)); // Does nothing });
promise.then(function (number) {
console.log('The number is ' + number);
});
// Console output:
// The number is 3.141592653589793
Running the code in this example demonstrates that the calls to reject(0) and resolve(Math.sqrt(-1)) have no effect because the promise has already been fulfil‐
led with a value for Pi.
The immutability of a settled promise makes code easier to reason about. Allowing the state or value to change after a promise is fulfilled or rejected would introduce race conditions. Fortunately, the state transition rules for promises prevent that problem.
Since the reject function transitions a promise to the rejected state, why does the resolve function transition a promise to a state called fulfilled instead of resolved?
Resolving a promise is not the same as fulfilling it. When the argument passed to resolve is a value, the promise is immediately fulfilled. However, when another promise is passed to resolve, such as promise.resolve(otherPromise), the prom‐
ises are bound together. If the promise passed to resolve is fulfilled, then both prom‐
ises will be fulfilled. And if the promise passed to resolve is rejected, then both promises will be rejected. In short, the argument passed to resolve dictates the fate of the promise. Figure 2-2 shows this process.
Figure 2-2. Resolving or rejecting a promise
The resolve and reject functions can be called without an argument, in which case the fulfillment value or rejection reason will be the JavaScript type undefined.
The Promise API also provides two convenience methods (see Example 2-7) for cre‐
ating a promise that is immediately resolved or rejected: Promise.resolve() and Promise.reject().
Example 2-7. Convenience functions for resolve and reject
// Equivalent ways to create a resolved promise new Promise(function (resolve, reject) { resolve('the long way')
});
Promise.resolve('the short way');
Promise States | 17
// Equivalent ways to create a rejected promise new Promise(function (resolve, reject) { reject('long rejection')
});
Promise.reject('short rejection');
These convenience functions are useful when you already have the item that should be used to resolve or reject the promise. Some of the code samples that follow use these functions instead of the traditional Promise constructor to concisely create a promise with the desired state.