1. Introduction to Asynchronous AngularJs
AngularJs, It is the most used front-end MVC framework. It is mostly used to create single page front-end application. When you have started working on AngularJS. You have to deal with the asynchronous call. In respect ti that you must come to know $http and $resource services. These both services are used to get server side data asynchronously. To handle an asynchronous call, you need to define callbacks. Deferred-Promises is a design pattern which helps you to deal with these callbacks.
Before starting Deferred & Promises in AngularJS, Let’s understand callback and why we actually need Deferred-Promises in AngularJS or any other language.
2. Callbacks
In JavaScript, a function is a special kind of object/ data type which you can pass as an argument. This is one of the most useful features of JavaScript programming language. When you call a function and you do not know when that function will return required result. Here you can pass a callback, which can be call when the result will come.
jQuery:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| ( function () { var successHandler = function (data) { console.info( 'Twitter\'s data: %j' , data) }; var ErrorHandler = function (error) { console.info( 'Twitter\'s error:' , error) }; $.ajax({ url: 'https://api.twitter.com/1.1/search/tweets.json?q=%23freebandnames&since_id=24012619984051000&max_id=250126199840518145&result_type=mixed&count=4' , data: { format: 'json' }, error: ErrorHandler, success: successHandler, type: 'GET' }); })() |
AngularJS:
1
2
3
4
5
6
7
8
9
10
11
| ( function () { var successHandler = function (response) { console.info( 'Twitter\'s data: %j' , response.data) }; var ErrorHandler = function (response) { console.info( 'Twitter\'s error:' , response.error) }; $http .get( 'https://api.twitter.com/1.1/search/tweets.json?q=%23freebandnames&since_id=24012619984051000&max_id=250126199840518145&result_type=mixed&count=4' ) .success(successHandler); })() |
Here in above example successHandler, ErrorHandler is callbacks which run when success or error event fire accordingly. These are the most basic example I found on the internet. Later on, we will learn how to handle these events efficiently in AngularJS.
3. Callback Hell
It is not a problem to work with $http/ $resource. The real problem arise when you have to deal with multiple callbacks which are dependent on each other. The result would be something like this-
Example:
1
2
3
4
5
6
7
8
9
10
11
12
| ( function () { asyncCall( function (err, data1) { if (err) return callback(err); anotherAsyncCall( function (err2, data2) { if (err2) return calllback(err2); oneMoreAsyncCall( function (err3, data3) { if (err3) return callback(err3); // are we done yet? }); }); }); })() |
This is known as callback hell. It is hard to understand and creates a lot of confusion.
4. Promise- A Better way of handling callbacks
By the definition, Promise is an interface which represents a proxy value. ie. We don’t know the value at the time it’s created. This promise will return value like any synchronous function in future. There can be several states for a promise.
- pending: initial state, not fulfilled/rejected.
- fulfilled: successful operation
- rejected: failed operation.
- settled: the Promise is either fulfilled or rejected, but not pending.
The promise is something like a man ask his son to get a weather forecast. And son returns a promise that he will go and get the weather forecast. There could be three possible outcomes:
- A) Weather forecast retrieved! Sunshine. // promise fulfilled +ve
- B) Weather forecast retrieved! Cloudy and rain. // promise fulfilled -ve
- C) Couldn’t get the weather forecast. //promise was rejected
Example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| ( function () { // function somewhere in father-controller.js var makePromiseWithSon = function () { // This service's function returns a promise, but we'll deal with that shortly SonService.getWeather() // then() called when son gets back .then( function (data) { // promise fulfilled if (data.forecast === 'good' ) { prepareFishingTrip(); } else { prepareSundayRoastDinner(); } }, function (error) { // promise rejected, could log the error with: console.log('error', error); prepareSundayRoastDinner(); }); }; })() |
For better understanding, you can refer to promise-as-cartoon.
5. Promise In Angularjs- $q Service
Promises in AngularJS is provided by $q service. It has reach feature like any other Promise library.
$q
service is a service that helps you to run your code asynchronously. As its documentation say, this is inspired by Chris Kowal’s Q library (github.com/kriskowal/q).Deferred Object:
Deferred is an object which exposes the promise. It has mainly three methods resolve(), reject(), and notify(). Deferred returns promise object. When Deferred completes, You call methods either resolve(), reject(), and notify() . It calls callback register to either resolve(), reject(), or notify() according to how it has completed.
Example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| function sayHelloAsync(name) { return function () { var defer = $q.defer() setTimeout( function () { //Greet when your name is 'deepak' if (name == 'deepak' ) { defer.resolve( 'Hello, ' + name + '!' ); } else { defer.reject( 'Greeting ' + name + ' is not allowed.' ); } }, 1000); return defer.promise } } |
Promise Object:
A promise is an object which is return by a Deferred object. You can register different callbacks for different events resolve(), reject(), or notify() and it will execute when the async function has completed.
Example:
1
2
3
4
5
6
7
| var helloPromise = sayHelloAsync( 'deepak' ); helloPromise.then( function (data) { console.log(data); }, function (error) { console.error(data); }) |
Deferred API:
A new instance of defer is created by calling $q.defer(). It has mainly three methods.
- .resolve(value) – This method is use to resolve the derive promise ie. // promise fulfilled -ve
- .reject(value) – This method is use to reject the derived promise ie. // promise fulfilled -ve
- .notify(value) – This method is used to notify the current state of the derived promise
Example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
| var getTodoList = function (url) { var deferred = $q.defer(); var data = [ { "task" : "I wanna to learn Web-Worker..." , "stared" : "nopes!!: (" , "estimatedTime" : "Infinity" }, { "task" : "I wanna to learn Monkey-Patching..." , "stared" : "Yup!!!" , "estimatedTime" : "1day" }, { "task" : "I wanna to learn ui-router" , "stared" : "nopes!!: (" , "estimatedTime" : "99999hr" } ]; var percentage = 0; var interval = $interval( function () { percentage = percentage + 5; deferred.notify({percentage: percentage}); if (percentage > 99) { $interval.cancel(interval); deferred.resolve(data) } }, 500) return deferred.promise; } |
Promise API
A new promise is created when you create a defer. You can get the instance of promise object by defer.promise. It is used to getting the result of the defer when a promise has completed. There are three events where you can bind your listeners.
- then(successCallback, errorCallback, notifyCallback)- The Regardless promise is resolved or rejected, it calls one of the success or error callback. Notification callback may be called zero or more than one time.
- catch(errorCallback)- This is the shorthand for the promise.then(null, errorCallback) where success callback is not given.
- finally(callback, notifyCallback)- This allows you to watch every single event of a promise. The callback is called once when either promise is resolved or rejected.
Example:
1
2
3
4
5
6
7
8
9
10
11
| var promise = getTodoList(); promise.then( function (data) { that.list = data; that.isVisible = true ; that.status = 'DONE' ; }, function (error /*Error event should handle here*/ ) { }, function (data /*Notify event should handle here*/ ) { that.status = 'PENDING' ; that.percentage = data.percentage; }) |
8. $http & $resource
$http and $resource are some of a good example of promise based services. Both these services support promise design patterns. Here using these module/services you can perform your ajax needs.
Since $http is an abstraction over $q, it has different callbacks. Instead of .then and .catch, it’s .success and .error and the arguments you get are different.
Example:
1
2
3
4
5
6
7
8
9
10
| $http.get( 'https://api.github.com/users/deepakshrma/gists' ) .then( function (response) { $scope.gists = response.data; }) . catch ( function (response) { console.error( 'Gists error' , response.status, response.data); }) .finally( function () { console.log( "finally finished gists" ); }); |
Similarly, $resource service returns a promise. It can be retrieve by simply $resource.get(‘somedata’).$promise.
Example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
| var commentPromise = CommentApi.update({id: comment._id}, comment); commentPromise.$promise.then( function () { $scope.alerts.addAlert( "Successfully updated approval status!" , "success" ); }, function () { $scope.alerts.addAlert( "Something went wrong. Please try again." , "danger" ); }); //Here in above CommentApi is a resource service angular.module( 'myapp' ) .factory( 'CommentApi' , function ($resource) { return $resource( '/v1/api/comments/:id/:controller' , { id: '@_id' }, { update: { method: 'POST' }, list: { method: 'GET' } }); }); |
9. Some other useful features- $q.all()
$q.all() is one of the method that i use more frequently. $q.all accepts array of promises as argument. Once all of the promises get completed. you will get the result in callback function as array of results.
Example:
1
2
3
4
5
6
7
| var promise1 = $http({method: 'GET' , url: '/api-one-url' , cache: 'true' }); var promise2 = $http({method: 'GET' , url: '/api-two-url' , cache: 'true' }); $q.all([promise1, promise2]) .then( function (data){ console.log(data[0], data[1]); }); |
No comments:
Post a Comment