The $q service included in AngularJS provides an API to deal with asynchronous functions and gives a way to synchronize them.
Let's talk first about the customAsyncFunc function that we've defined in our script file. The basic idea behind it is to have a function that simulates the execution of an asynchronous task and notifies about the progress during the execution and about the final result. The function simulates the execution of 2 steps taking 1 second each and then, after a custom total duration, notifies about the final result with a successful execution or a failure. The custom duration must be at least 3 seconds to allow the steps to have enough time to complete. The $q service provides the defer method that returns a deferred object. We can see it as the representation of a task that will be completed in the future. The deferred object exposes the resolve, reject and notify methods that allow us to respectively notify that the task has completed, that it failed or that there has been some progress in the execution. Each one of these methods allows to pass a value (a string, a number or any object) back to the listeners of the execution events. The customAsyncFunc function returns a promise object which can be seen as a placeholder for the result that will be available at the end of the asynchronous task execution. The promise object exposes some methods to allow the interested listeners to register some handlers that will notify them about the execution progress and final result.
In point 1 of the example, we see the basic structure of a call to an asynchronous function and the registration of the event handlers to be notified about its execution. We can invoke the then method of the promise returned by the asynchronous function to register a success, a failure and an update handler. Each one of them takes a single parameter with the value sent by the asynchronous function. The success handler takes the value passed to the resolve method of the deferred object, the failure handler takes the value of the reject method and the update handler takes the value of the notify method. When the customAsyncFunc function is called we have the choice of simulating a success or a failure to see the different outcome.
In point 2 we see that we can register multiple callbacks to the same promise and be notified about the same events in all the provided handler functions.
Point 3 shows alternative ways of registering our handlers. The then method of the promise registers only the success and update handlers while it doesn't register the failure one because we show that we can register it with the catch method (it is just a shortcut for then(null, failureHandler, null)). If we wanted to just listen to the success event, we could have just written then(successHandler) without specifying the other parameters. Here we also have the finally method that is useful to register a finalization handler that will be called at the end of the execution both in case of success or failure (the finalization handler is called after the registered success and failure handlers).
Having to wait for an asynchronous function to end before the next one starts is a typical need when the output of the first function is needed as input of the second. Point 4 of the example shows how we can chain multiple asynchronous functions by chaining their promises. Here we have two functions, asyncFunc1 and asyncFunc2. They take an array as input, the executionPath array, just to make it easy to specify which function we want to be successful or fail (the first element of the array tells the outcome we want from asyncFunc1, while the second one tells the outcome of asyncFunc2). When asyncFunc1 is called, it takes executionPath as input and if successful sets the first element of the array as SUCCESS and passes executionPath to the next function by returning it as a result with the resolve method. asyncFunc2 acts as success handler of asyncFunc1 and receives as input asyncFunc1's output. The logExecutionResult is useful in any case we just want to log an asynchronous call outcome (it acts as a generic handler). If asyncFunc1 fails, the logExecutionResult is called twice, the first time as a failure handler of asyncFunc1 and second time as a failure handler of asyncFunc2 because the error is propagated to all the failure handlers until the last one (the value returned by logExecutionResult is passed as input of the next failure handler).
If we need to wait for multiple asynchronous functions to end and want to be notified when all of them ended, we can use the all method of the $q service. Point 5 of the example shows that we can call 3 asynchronous functions with a different execution duration and have a single success and failure handler. When a function fails, the failure handler is called with the value returned by the reject method called by the function that failed first. When all the asynchronous functions are successful, the success handler receives as input an array containing all the success values of all the functions where every element in the array corresponds to the success value of the function in the same position in the array passed to the all method.
Point 6 is like point 5, just with a little difference. The $q service provides the when method that is useful to wrap any value in a promise. We might need this if the want to mix promises from asynchronous functions with already known values. The when method creates an already resolved promise.
In point 7 we see that the $q service can be very useful also to manage asynchronous calls made to a server through the $http service. In fact, the methods provided by the $http service return promises augmented with other properties and methods, but they are still promises and so they can be used with the $q service. Here we see that we can wait for 3 different GET server calls that take a different time to complete.