Thomas: Promise chains allow us to manage sequences of asynchronous requests and responses. To demonstrate techniques for changing promises, I prepared a test application called The Flight Demo. The flight demo displayed here in the browser will show flight departure, plane information, and a weather forecast.
While the page view is itself visually trivial, the real asynchronous work is done under the hood. I've already prepared the Mock data services where you see here both the travel service and the weather service have API's that return promises.
Here we see that the travel service Get Departure returns a promise that is already resolved with mock data that contains the user's upcoming travel information. Now let's get the flight dashboard controller working so the travel information is shown in the dashboard view.
First we need to load the user's departure information from the travel service. The controller uses the injected instance of the travel service to call Get Departure and load the user's departure information. Since Get Departure returns a promise, we attach a success handler that will be called when a promise is resolved. And that promise is resolved after the asynchronous response occurs.
Our success handler is past the value that was used to resolve the promise. In this case, it is the departure information object which we will then publish to the scope.
Let's refresh the flight demo page in the browser. Now we see the user's departure information. However, the page still does not show the plane information, nor the weather forecast. Notice a dependency here if we look back at the data services. Request to load the plane and weather information use departure information. So to get the flight, we need the flight ID. So this means that we must first load the departure information before we can load the plane and weather information.
Since each of those asynchronous service calls also returns a promise, this means we must build a chain of promises. We can visualize the chain of promises as follows: Get Departure is called and then Get Flight is called, and then Get Forecast is called.
Notice that the Get Flight call needs the flight ID provided in the departure information. And likewise, the Get Forecast call needs the departure date.
To get the flight and weather information for the flight demo page, let's make those service calls and use the return promises to update our scope.
Notice how the success handler for the Get Flight is past the flight object. And the success handler for the get forecast is past the weather object, both of which are published to the scope.
Refreshing our page shows the flight and forecast information is now displayed. But wait. The weather information is not showing. Let's take a look at the console and see that we forgot to tell Angular to inject the weather service. So let's fix that real quick.
Refreshing the page again now shows all the information as expected. This approach works and is an example of deeply nested promise chains. Nesting promise chains quickly become messy, however, if you have lots of logic. Is there another way to build our chain of promises?
What if we viewed each request response as a self-contained process? Then we could chain processes. On the right is the refactored flight dashboard controller. But now we have three intuitively named functions-load departure, load flight, and load forecast, all chained together in a flight chain as we see here in the call.
Each of these functions internally makes a service call, gets a promise, and attaches a success handler to the promise. And each handler publishes something to the scope. But two other very important things are now happening. First, notice that each of the segments-load departure, load flight, and load weather returns a promise.
The important thing to realize here is that instead of returning a data object, we are returning another promise. Returning promises allows us to build chains where each segment is only resolved when the promise at that segment resolves. And that promise could itself represent a sub chain.
While a segment is waiting for its promise to resolve or reject, all the remaining segments in that chain are waiting. And, in fact, those segments have not even been called yet. The async request in subsequent segments are queued and haven't even been called yet.
This is promise chaining. This is very powerful. Second, notice that the internal promise success handler, for example, in travel service, get departure, the success handler is here. Notice that this success handler of each segment returns a value; a value that may be passed as an argument value when invoking the next segment of the promise chain.
So get departure's success handler is returning the flight ID, and that value is passed in when we are making the next segment call to load flight.
And while Load Flight returns the flight object, the next segment, Load Weather, ignores that value because it doesn't need any of the flight information. This is an example of a flattened promise chain. And it is now really easy to understand and to manage.
For even more solutions with promise chaining and information on managed promises and rejections, check out the GetHub repository at https://github.com/ThomasBurleson/angularjs-FlightDashboard.