Promises

IN THEORY AND  REALITY


 mlmorg - 2014

"OH GOD, KILL ME NOW"

 

Difficult to explain

What the fuck is a monad anyway?

Over-hyped


"PROMISES ARE THE FUTURE"

willyd - every day

Not the solution to

"Why stop at lists!  Promises in a box!  Promises with a fox!  Attributes are promises!  Arrays are promises!  Everything is a promise!"

LIFE

Drew Crawford  - 2013

THE

UNIVERSE



AND


EVERYTHING

What Are promises?


promises/a+


  • Have a then method which returns a promise
  • Maintain state: 'pending', 'fulfilled', 'rejected'
  • Must follow the promise resolution procedure




This means promises are  implementation agnostic* .
 doSomething().then(doSomethingAfter);

PRomises can be resolved or rejected


function foo(bar){
  try{
    var result = bar.toLowerCase();
    return Promise.resolve(result);
  }
  catch(error){
    return Promise.reject(error);
  }
} 
var loadFoo = foo('THE FUTURE');

loadFoo.then(function(result){
  console.log(result);
}); 

A resolved promise calls onResolved, a rejected one calls onRejected

When resolved, State is passed down the chain


This allows easy chaining of promises:
function foo(){
  return Promise.resolve('bar');
};

foo().then(function(result){
  return result.toUpperCase();
})
.then(function(result){
  return result.replace('R', 'T');
})
.then(function(result){
  console.log(result);
}); 

 

brilliant


So why should I give a shit?
 

solve problems on the syntax level

avoid 'callback hell'


function serveDrink(order, callback){
  findBottle(order, function(bottle){
    openBottle(bottle, function(){
      findGlass(function(glass){
        fillGlass(glass, bottle, function(drink){
          giveToCustomer(drink, function(){
            callback();
          });
        });
      });
    });
  });
} 

When your async code uses promises, you can write:

findBottle(order)
.then(openBottle)
.then(findGlass)
.then(fillGlass)
.then(giveToCustomer); 

Simplify error handling


function serveDrink(order, callback){
  findBottle(order, function(err, bottle){
    if(err) handleError(err);
    openBottle(bottle, function(err){
      if(err) handleError(err);
      ...
    });
  });
} 

You can catch errors from anywhere in the chain:

findBottle(order)
.then(openBottle)
.then(findGlass)
.then(fillGlass)
.then(giveToCustomer, handleError); 

Remove awkward PARALLELISM


var count = 0;
var onFinished = function(finishedFunc){
  count++;
  if(count == 2){
    finishedFunc();
  };
};

var order1 = serveDrink('rum', onFinished);
var order2 = serveDrink('vodka', onFinished); 

We can use '.all' which resolves when all promises passed to it resolve

var order1 = serveDrink('rum');
var order2 = serveDrink('vodka');
Promise.all([order1, order2]).then(finishedFunc);

A common misconception


Promises were not designed to solve these problems.

They are merely a nice side-effect of modelling the problem
 in a semantically different way.

Solve problems on the Semantic level

 

"The nature of promises is that they remain immune to changing circumstances"

Frank Underwood, House of Cards - 2013

Maintain encapsulation


Promises maintain state where callbacks do not:

  • Registering a function guarantees it's execution.
  • Order of execution is the order of registration.

Logger.prototype.logResult = function(promise){
  return promise.then(function(result){
    console.log(result);
  }
} 

This function has absolutely no knowledge of the action being
taken, but 'hooks in' to the chain without interfering with the result.

Composition in an async world



In a chain of synchronous calls, composing functions
 and catching exceptions is easy.


Promises bring that ability to asynchronous calls. 

Promises give us back functional composition and error bubbling.

functions for functions, 

not flow control


A callback is using a function to control flow.

We can instead control the flow with promises;
 leaving functions to do one thing, and do it well.
WTF was I talking about in that last section? 

Promises in Practise


functional composition demo

Encapsulation / flow control demo

Promises in Action



Introducing



HTTPDeferred wraps a jQuery Deferred and is designed to 
simplify error handling when working with a RESTful JSON API.  

  • handle - Handle a given HTTP status code/codes
  • unhandled - Handle only errors which don't have a handle or fail function

var updateName = API.updateName('will');  
updateName.fail(function(reason){  
  var errorCode = reason.status;
  if(errorCode == 400){
    handleFormError(reason.responseText);
  }
  else if (errorCode == 403){
    handleUnauthorisedUser(reason.responseText);
  }
  else if (errorCode == 500{
    handleServerError(reason.responseText);
  }
  else {
     // Success
  }
});
var updateName = API.updateName('will');
updateName.handle([400], handleFormError);
updateName.handle([401, 403], handleUnauthorisedUser);
updateName.handle('5XX', handleServerError);
updateName.done(function(){
  // Success
});
HTTPDeferred

In Summary


  • A nicer syntax and interface for asynchronous code.
  • Compose functions as if they were synchronous
  • Abstract flow control out of our function logic
  • Help us maintain separation of concerns.

Further reading




Promises. In theory and reality

By Will Demaine

Promises. In theory and reality

  • 2,060