Thursday, 2 August 2018

AngularJS Deferred & Promises- Basic Understanding

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 serviceIt has reach feature like any other Promise library. $qservice 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