Promises in AngularJS. Part I. Basics.
There are many blog posts about Promises in general and Promises in AngularJS in particular. Promises are a part of the ES6 (EcmaScript 6) specification, so it is worth to learn them.
In this blog post, I will try to summarize the most important info about Promises in a simple and clear way. The necessary code snippets will be provided too. The first part is about basics.
What is a Promise? Promise is a solution to avoid so-called “Pyramid of Doom“.
If you work with asynchronous operations (HTTP calls, file system operations, etc.), you know the situation where you have callbacks nested inside of another callbacks which are nested again inside of callbacks and so on. An example:
asyncOperationOne('first data', function(firstResult) { asyncOperationTwo('second data', function(secondResult) { asyncOperationThree('third data', function(thirdResult) { asyncOperationFour('fourth data', function(fourthResult) { doSomething(); }); }); }); });
Every asynchronous operation invokes a callback function when the operation is completely done. In case of AngularJS and the $http service the “Pyramid of Doom” could look as follows:
var result = []; $http.get('/api/data/first.json').success(function(data) { result.push(data); $http.get('/api/data/second.json').success(function(data) { result.push(data); $http.get('/api/data/third.json').success(function(data) { result.push(data); $scope.result = result; }); }); });
We didn’t show yet the error handling. Now imagine how it looks like with error handling. Terrible and not readable. Promises help to synchronize multiple asynchronous functions and avoid Javascript callback hell. The official concept of Promises is described in the Promises/A+ specification. With promises, an asynchronous call returns a special object called Promise.
The calling code can then wait until that promise is fulfilled before executing the next step. To do so, the promise has a method named then, which accepts two functions – success and error function. The success function will be invoked when the promise has been fulfilled. The error function will be invoked when the promise has been rejected. An example of $http service based on promises:
$http.get('/api/data.json').then( function(response) { ... }, function(error) { ... } );
We can say, a promise represents the future result of an asynchronous operation. The real power from promises is the chaining. You can chain promises. The return value of some promise’s callback is passed as parameter to the callback of the next promise. In this way, you can access previous promise results in a .then() chain. An error is passed too, so that you can define just one error callback at the end of chain and handle all errors there. Here is an example of promise chaining:
$http.get('/api/data.json').then( function(response) { return doSomething1(response); }) .then(function(response) { return doSomething2(response); }) .then(function(response) { return doSomething3(response); }, function(error) { console.log('An error occurred!', error); } );
Be aware that either the success or the error callback will be invoked, but never both. What to do if you need to ensure a specific function always executes regardless of the result of the promise? You can do this by registering that function on the promise using the finally() method. In this method, you can reset some state or clear some resources. An example:
$http.get('/api/data.json').then( function(response) { // Do something in success case }, function(error) { // Do something in failure case }).finally(function() { // Do something after either success or failure } );
Now we got rid of nested callbacks (“Pyramid of Doom”). Where and how to use the promise examples above? A typical pattern in AngularJS is to have calls via $http in a service. Controllers call services and are not aware that $http is used. Example for MyController -> MyService -> $http:
// In MyService this.fetchData = function() { return $http.get('/api/data.json'); }; // In MyController $scope.fetchData = function() { MyService.fetchData().then(function(response) { return response.data; }, function(error) { ... }).finally(function() { ... }) };
We have to mention yet that AngularJS’ implementation of promises is a subset of the library Q. Q is the most known implementation of the Promises/A+ specification. That’s all for this short post. In the next post, we will meet the AngularJS’ deferred object.
A deferred object is an object that exposes a promise as well as the associated methods for resolving / rejecting that promise. It is constructed using the $q service – the AngularJS’ implementation of promises / deferred objects inspired by Q.
Stay tuned!
Reference: | Promises in AngularJS. Part I. Basics. from our WCG partner Oleg Varaksin at the Thoughts on software development blog. |