Working With Promises In AngularJS
Working with Angular (and JavaScript in general) means you really need to understand asynchronous code. The default way of handling this in Angular is to use promises - no more messy callbacks.
A promise is basically an object with a then
method that represents an operation whose result may be unknown. Whoever has access to the promise can use the then
method to attach callbacks in order to be notified when the operation completes.
Handling Errors in Promise Chains
While promises are great, they also make it rather easy to swallow errors. Using promises means we don’t need to handle errors if we are returning the promise to the caller. This usually makes a lot of sense, for example we might make a request to the server to fetch some data - it is unlikely that we know how the UI should handle a failure at the request level, but the calling code should know how to handle this - and should therefore handle the failure.
In this example, no error handler is attached to the promise returned by fetchUser
. If the operation fails the error will simply disappear. A simple way to handle this would be to alert the error to the user.
Re-throwing Errors
Let’s take a look at what the userService
might look like.
The fetchUser
function does not need to register a callback, since it is returning the promise to the caller - the caller is expected to handle any possible errors.
However, what if we wanted to log any errors that happen during this step? (I’m just using console.log
here, but this could be a logging service like HoneyBadger.) We could possibly implement logging like this:
This implementation will cause massive headaches - if an error occurs it will be logged (as expected), but then the promise will be resolved with the null
value being returned by the logError
function! In order to propagate the error up the chain we need to return a rejected promise (instead of the null
value we are currently returning).
Now the error will be logged and the original caller will still receive a rejected promise, which will trigger the error callback (and display the error to the user in this case).
More features of $q
The $q service has a few more features that are very useful when dealing with asynchronous code. For example, we might want to show a loading indicator to the user when we trigger the request, but we need to ensure that the loading indicator is always hidden when the promise completes (so both for failure and success). We can do this with the finally
function.
You can find all the code on Plunker. Happy coding.