Understanding Asynchronous Invocation With Promise Using AngularJS
When you invoke a function asynchronously, it does not block or wait for the response to come from the server. It returns almost immediately. The status of the async function invocation then can be determined using callbacks. All this is achieved using Promise object.
The article will demonstrate how one can make use of promise object with asynchronous invocations using AngularJS.
A promise can be thought of as a future object that has its data populated at a later time. It makes your asynchronous call look more like a synchronous process. The call made to asynchronous function will instantly return you the promise object that will act more like a proxy to the actual data which will be available at a later time. To make an asynchronous call, you typically use AJAX feature. When you make an AJAX call to the remote server, it instantly returns a promise object and not give you the actual response from the server.
This promise object is empty and will not have any data. Once the response comes from the server, this promise object will be populated or resolved with the response data. The response data can signify either a successful execution or throw an error, in which case the promise object is rejected with an error message. You make use of callback functions associated with the promise object to determine whether the server responded with success or error data. The below figure shows the working model of asynchronous method invocation.
Let’s make the practical use of the promise object through ‘verify user’ use case. Often during any registration process, user id is verified for its existence. If the user id is not found, then the system displays the message that this user is available and if found then that user id is already registered and not available. We will verify user id asynchronously using AJAX call. Promise objects are handled by the built-in AngularJS service called $q
. The below code shows the controller that makes use of $q
service and defines verifyUser()
function that will return the promise object.
angular.module('myApp.controllers', []). controller('RegistrationController', function($scope, $q) { $scope.verifyUser = function() { var userId = $scope.user; var deferred = $q.defer(); var user = findUserFromDB(); // This method can return true or false if (user) { deferred.resolve('User ID is available'); } else { deferred.reject('User ID already taken, try another name'); } return deferred.promise; } });
Let’s walk-through the code. First we get the user id input from the UI using $scope
variable. Then we call $q.defer()
that returns the new deferred object. This deferred object holds the promise object. Then we check against the DB whether the user id exists or not. To keep it simple, we are calling a hypothetical function findUserFromDB()
and assume that it returns true if the user is available (not existing in DB) and false otherwise.
The function findUserFromDB()
will be an AJAX call to the remote DB server. If the server returns the value of true, the associated promise of the deferred object is resolved by calling defer.resolve()
method. If the user id already exists, the server will return false, in that case the promise is rejected with an error message by calling defer.reject()
method. The last line of the code returns the associated promise object. The promise object is returned almost instantly when the function verifyUser()
is invoked.
Now let’s make use of this promise object. The below code shows the use of promise object by defining different callback functions that are invoked based on whether the promise is resolved or rejected. We define a method named checkUser()
that will call our verifyUser()
method to get the promise object. The checkUser()
method will be invoked once the user clicks on Verify button from the UI.
angular.module('myApp.controllers', []). controller('RegistrationController', function($scope, $q) { ... $scope.checkUser = function() { var promise = $scope.verifyUser(); promise.then(function(msg) { $scope.message = 'Resolved ' + msg; }, function(msg) { $scope.message = 'Rejected ' + msg; }); } });
Once the promise object is obtained using verifyUser()
method, we then invoke the then()
method of the said object. The then()
function takes three callback functions as parameters. First callback is invoked when the promise is resolved using defer.resolve()
. This callback can take the success value as a first argument if the value is passed to defer.resolve()
. The second callback is invoked if the promise is rejected using defer.reject()
. Again this callback can take a failure value as an argument, if passed to defer.reject()
. The third callback is a notification callback that is invoked using defer.notify()
.
The notification callback is helpful when you want to see the status of your request at regular interval until the final response is received from the server. Notification callback can be called multiple times. For this example, we are only using the first two callbacks. The first two callbacks are invoked only once and either of the two will be invoked based on whether the promise object is resolved or rejected.
Now let’s get the whole thing into action. Our UI template will look like the following:
<html lang="en" ng-app="myApp"> ... <body ng-controller="RegistrationController"> <input name="userId" ng-modal="userId" /><br/> <button ng-click="checkUser()">Verify</button><br/> <strong>{{message}}</strong> <script src="lib/angular.min.js"></script> <script src="js/app.js"></script> <script src="js/controllers.js"></script> </body> </html>
If the user clicks the Verify button, either of the first two callbacks will be called depending on the return value of the findUserFromDB()
method. The status message, whether successful or not, will be displayed below the button. The status message will be part of $scope.message
variable.
Now you see how the asyncronous function makes use of promise object to make it look all like a synchronous affair. So make a promise! if you are going an asynchronous way.
Reference: | Understanding Asynchronous Invocation With Promise Using AngularJS from our WCG partner Rajeev Hathi at the TECH ORGAN blog. |