Promises in AngularJS

Father and Son

One morning, a father says to his son:

"Go and get the weather forecast, son!"

 

  • Son creates a promise with his dad at the door when he leaves

  • The dad decides if the weather tomorrow is good:

    • ​He will go for fishing
    • He will stay back

Outcome

A) Weather forecast retrieved! Sunshine :-)

( promise was fulfilled )

Outcome

B) Weather forecast retrieved! Cloudy and rain :-(

( promise was fulfilled )

Outcome

C) Couldn't get the weather forecast :-/

(promise was rejected!)

 // 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();
                });
        };

Controller: FatherCtrl

 app.factory('SonService', function ($http, $q) {
        return {
            getWeather: function() {
                // the $http API is based on the deferred/promise APIs exposed by the $q service
                // so it returns a promise for us by default
                return $http.get('http://fishing-weather-api.com/sunday/afternoon')
                    .then(function(response) {
                        if (typeof response.data === 'object') {
                            return response.data;
                        } else {
                            // invalid response
                            return $q.reject(response.data);
                        }

                    }, function(response) {
                        // something went wrong
                        return $q.reject(response.data);
                    });
            }
        };
    });

Service: SonService

Javascript :single threaded

Javascript :single threaded

console.log("1");
setTimeout(function(){console.log("2");},3000);
console.log("3");
setTimeout(function(){console.log("4");},1000);
Javascript executes each line in sequence.

So you told js:

write 1:  js writes 1
wait 3 seconds and then write 2:  ok I'll wait 3 seconds...now what?
write 3: ok I'll write 3, by the way, the 3 seconds is not up.
wait 1 second and then write 4:  ok I'll wait 1 second...
then js waits .99999 seconds ... and writes 4

then waits some more and writes 2

What is a promise?

An assurance that we will get a result from an action at some point in the future, let’s see the two possible results of a promise:

  • A promise is said to be fulfilled when we get a result from that action (meaning that we get a response, regardless of whether the response is good or bad)

 

  • A promise is said to be rejected when we don’t get a response(for instance if we were retrieving some data from an API and for some reason we never got a response because the API endpoint was down etc.)

Why Promises?

We need promises because we need to make decisions based on the possible results of our call

$q????? 

  • This $q service is heavily inspired by the Q library of Kris Kowal.
  • $q service that provide deferred and promise implementations.
  •  it will let you write your custom promises as well (so you can resolve or reject a promise when appropriate).
  • Deferred represents a task that will finish in the future
  • A number of Angular services return promises:    $http$interval$timeout,...........

 

better die.......

Deferred 

 

  • Initially this task is in the status pending.
  • Eventually the task completes successfully or it fails.
  • In the former case the status of deferred will be resolved while in the latter case the status will be rejected
var deferred=$q.defer();
var deferred = $q.defer();
// deferred contains the promise to be returned
// to resolve (fulfill) a promise use .resolve
deferred.resolve(data);
// to reject a promise use .reject
deferred.reject(error);

semantics

// this
$http.get('/api/v1/movies/avengers')
  .success(function(data, status, headers, config) {
     $scope.movieContent = data;
  });
 
// is the same as
var promise = $http.get('/api/v1/movies/avengers');
 
promise.then(
  function(payload) {
    $scope.movieContent = payload.data;
  });

Promises and Services

Subtitle

angular.module('atTheMoviesApp', [])
  .controller('GetMoviesCtrl', 
    function($log, $scope, movieService) {
     $scope.getMovieListing = function(movie) {
       var promise = 
           movieService.getMovie('avengers');
       promise.then(
          function(payload) { 
              $scope.listingData = payload.data;
          },
          function(errorPayload) {
              $log.error('failure loading movie', errorPayload);
          });
     };
  })
  .factory('movieService', function($http) {
    return {
      getMovie: function(id) {
         return $http.get('/api/v1/movies/' + id);
      }
    }
  });

What about post processing??

separation of concerns

Subtitle

...
.factory('movieService', function($http, $log, $q) {
  return {
   getMovie: function(movie) {
     var deferred = $q.defer();
     $http.get('/api/v1/movies/' + movie)
       .success(function(data) { 
          deferred.resolve({
             title: data.title,
             cost: data.price});
       }).error(function(msg, code) {
          deferred.reject(msg);
          $log.error(msg, code);
       });
     return deferred.promise;
   }
  }
 });

Handling problems in nested service calls

this.getMovie = function(movie) {
    return $http.get('/api/v1/movies/' + movie)
           .then(
              function (response) {
                return {
                   title: response.data.title,
                   cost:  response.data.price
                });
              },
              function (httpError) {
                 // translate the error
                 throw httpError.status + " : " + 
                       httpError.data;
              });
};

...and now the error returned will be a single string, not the $http error with data, status, headers and config properties.

Doing more than one thing at a time

$q.all lets you trigger several callbacks at the same time, and use a single then function to join them all together.
service('asyncService', function($http, $q) {
      return {
        loadDataFromUrls: function(urls) {
          var deferred = $q.defer();
          var urlCalls = [{ url: 'ajax1.html' },{ url: 'ajax2.html' },{ url: 'ajax3.html' }];
          angular.forEach(urls, function(url) {
            urlCalls.push($http.get(url.url));
          });
          // they may, in fact, all be done, but this
          // executes the callbacks in then, once they are
          // completely finished.
          $q.all(urlCalls)
          .then(
            function(results) {
            deferred.resolve(
             JSON.stringify(results)) 
          },
          function(errors) {
            deferred.reject(errors);
          },
          function(updates) {
            deferred.update(updates);
          });
          return deferred.promise;
        }
      };
    });

Referred Links

Promises

By mohammad sirajuddin Rayyan

Promises

How promises works

  • 200