In this article I will provide some details about how $q promise chaining works in Angular.
Generally, promise handling is simple in Angular – all you need is a success handler and an error handler. However, a great feature of promises is the ability to chain handlers.
Most developers are familiar with promise handling in its simplest form:
The above code calls a service that returns a promise and “chains” on a then handler to the original call.
It turns out that we can easily extend this by continuing the chain by tacking on a second then:
This seems simple enough, but what is going on under the hood here? It turns out that we are dealing with a chain of promises – starting with the initial promise returned by getArticles(). Down the chain, each then invocation returns a new derived promise that becomes part of the chain. Each derived promise is resolved with the return value of each then function.
In the example above the first then returns the number of articles. This effectively will cause the first derived promise to be resolved with an integer value === articles.length. The promise resolve value/object is available as input to the next then method. This is true even if the then method doesn't explicitly return something. In that case the input to the next then method is undefined, but the resolve part works the same way. Think of it as resolving a promise with an optional input argument.
The example below shows an example where the second then doesn't return an explicit value. The derived promise will still resolve, but the callback of the subsequent than does not have an input argument defined.
Aside from the undefined data value in the third then function there is more going on here. The final then returns an explicit promise by calling getArticles() again. This shows that you're not limited to built in derived promises, but can also return promises generated by other sources midstream. This is powerful since it allows us to integrate external and internal promises in the same continuous chain.
The above example is naive in that it assumes a successful chain without accounting for error scenarios. To improve on this we should add error handling to the mix.
Error handling can be added through catch methods. You can either add a granular catch handler to deal with specific promises or you can add a single catch all at the end. The main difference is that specific error handlers midstream will handle the error and let subsequent then handlers carry on as if nothing happened – unless you return a rejected promise or throw an error from the catch function. The catch function may seem like a different beast, but it's really just a then method in disguise. In fact the then method actually supports two callbacks, so it's full potential is
The fist callback is the success handler described above and the second callback is the error handler.
Since the dual callback situation is somewhat cumbersome, catch was defined as a wrapper that maps to
Finally, here is an example with both a then handler and a catch handler