Why Promises ?

Let's step back to the basics

Single thread synchronous model aka. blocking







  • Suppose a program consisting of 3 distinct tasks
  • Tasks are performed one at a time
  • Always in a defined order
  • Simplest style of programming

Multi threaded model







  • Tasks run in separate threads
  • Threads are handled by the OS
  • Threads can run concurrently
  • They often end up just waiting
  • Or interleaved together in single CPU
  • Tasks might get suspended arbitrarily by OS
  • Thread communication and synchronisation is a diffcult topic

Single thread asynchronous model = Javascript







  • Tasks interleaved inside the same Thread
  • Goodness of synchronous + multithreaded
  • Programmer in control, not the OS
  • Tasks are not suspended arbitrarily

Single thread asynchronous model = Javascript




The event loop handles task scheduling



// Event loop pseudo code
for(;;) { 
  if (endOfProgram)
    quit();
  if(eventToHandle) { // task waiting, task finished ...
    handleEvent(eventToHandle); // Callback 
  }
}

Single thread asynchronous model = Javascript




Therefore



  • Each task must be non blocking
  • Otherwise you get freezes and lags

Stupid Asynchronous API: Callbacks

Asynchronous = Non Blocking

Callbacks are a way to make tasks non blocking


        task1(function task2(task1Result) {
          // do something with task 1 result 
        })

        var task1 = function(callback) {
          result = longRunningTask(); // Blocking call
          callback(result); // Calling a passed callback
        }
      

Stupid Asynchronous API: Callbacks

Asynchronous = Non Blocking

Callbacks are messy


        $('#button').click(function () {
          askForTwitterHandle(function(twitterHandle) {
            twitterHandle.getTweets(function (tweets) {
              processTweets(function (processedTweets) {
                ui.show(processedTweets);
              });
            });
          });
        });
      

Callbacks error handling is worse, no exception propagation

        $('#button').click(function () {
          askForTwitterHandle(function(twitterHandle) {
              twitterHandle.getTweets(function (tweets) {
                ...
              });
          }, function onFailure(error) {
            handleError(error);
          });
        });
      

In the synchronous model we have try/catch

        try {
          handle = askForTwitterHandle();
          tweets = handle.getTweets();
          ...
        } catch (errorMessage) {
          console.log('I messed up');
        }
      

Promises are the right abstraction

Instead of calling a passed callback, we return a promise



        ajax('template.html', function(err, template) {
            if (err)
              handleError();

            render(template);
        })

        // Becomes

        var promiseForTemplate = $http.get('template.html');
        promiseForTemplate.then(render, handleError)
      

Promises guarantees

          promiseForTemplate.then(onFulfilled, onFailed);
      


  • Only one of onFulfilled or onFailed will be called
  • onFulfilled will be called with a single value (return value)
  • onFailed will be called with a single rejection reason (thrown exception)
  • If the promise if already settled, the handlers are guaranteed to be called anyway
  • The handlers will always be called asynchronousy

Promises can be chained

          //
          var transformedPromise = promiseForTemplate.then(onFulfilled, onFailed);
      


  • If onFulfilled returns a value
    • transformedPromise is resolved with that value
  • If onFulfilled returns a promise
    • transformedPromise will adapt it's state
  • If onFailed throws an exception
    • transformedPromise will be rejected with that exception

Example



Sync

        var user = getUser();  // blocking, ie, prompt for password
        var username = user.name;
        


Async with Promise

          var userNamePromise = getUser().then(function (user){
            return user.name;
          })

Example: throwing an exception



Sync

        var user = getUser();  // blocking, ie, prompt for password
        if (user === null)
          throw new Error('null user');
        


Async with Promise

          var userPromise = getUser().then(function (user){
            if (user === null)
              throw new Error('null user');
            return user;
          })
        

Example: handling an exception



Sync

        try {
          updateUser(data);
        } catch (exp) {
          console.log('There was an error', exp);
        }

Async with Promise: exception propagation


          var updateUserPromise = updateUser(data).then(undefined, function(exp) {
            console.log('There was an error', exp);
          })
        

Example: exception propagation

Callbacks

          getUser('spike', function(err, user){
            if(err) {
              ui.error(err);
            } else {
              getBestFriend(user, function(err, friend){
                if (err) {
                  ui.error(err);
                } else {
                  ui.showBestFriend(friend, function(err, friend){
                    if (err) {
                      ui.error(err);
                    } 
                  })
                }
              })
            }
          })

Example: exception propagation




Promises

          getUser()
          .then(getBestFriend)
          .then(ui.showBestFriend)
          .then(undefined, ui.error)
        

Promises in Angular

  • Provided by $q service
  • Inspired by the the promise/deferred Q library
  • Promises are used everywhere Angular needs async
    • $http
    • $resource
    • $routeProvider
    • Controller properties
    • Response Interceptors
    • ...
  • Is a lightweight implementation of Promises (Q is much more complete)
  • Optimized for the Angular run loop

Angular Run Loop



How to use Promises in Angular


          .controller('testCtrl', function($scope, $q) { // inject the service

            var promiseOfTask = function () {

              var deferredTask = $q.defer(); // create a deferred object

              var result = blockingTask(); // long running task
              deferredTask.resolve(result) // resolve the future promise with the result

              return deferredTask.promise // return the promise
            }
             
            //using the promise 
            promiseOfTask().then(function(result){
              $scope.updateSomeModel(result);
            })
          })
        

Demonstration

Handling failovers for api calls

Resources: