Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Callbacks are calling back

Callbacks are calling back

Nested callbacks get really cumbersome after a while. Let's see some alternatives for a better Future of JS.
I Promise it will make your js code nicer.

Bruno Lara Tavares

April 24, 2013
Tweet

More Decks by Bruno Lara Tavares

Other Decks in Programming

Transcript

  1. var $as = $(“a”);! ! $as.on(“click”, function(e) {! /* do

    some stuff now that click happened */! });!
  2. Evented IO + CPS •  Processes happens as events happens

    – Waiting for a result is not feasible •  Continuing the process in the future –  Passing it to future processes
  3. Now we know the reasons to use a callback… Let’s

    see some different approaches to deal with future data
  4. Futures •  Deferred process handling var data = Future(function (handler)

    { /* do http request */ });! ! handleUserInterface();! putValuesOnTheFields(data.getValue()); !
  5. Broken implementation function Future(p) {! var value, finished;! finished =

    false;! process.nextTick(function() { p(function(result) { ! " "value = result;! " "finished = true; })});! ! return { ! getValue: function getValue() { ! if(!finished) {! console.log("not ready");! arguments.callee();! }! return value;! }! }! };! ! ! var p = Future(function(end) { setTimeout(function() { end(3); }, 1000) });! p.getValue()! !
  6. Working implementation of Future •  Async I/O + lack of

    threads makes it really complicated to use while loops in Javascript with background processes http://code.activestate.com/recipes/ 84317/
  7. Promises •  They are similar to Futures, in the sense

    of deferring execution. •  The interface allows you to create utility functions to combine and chain multiple promises •  (Shhhh! Promises are monads.)
  8. JQuery 1.5 ajax object var req = $.get('foo.htm');! ! req.done(function(response)

    {! // do something with the response! });! ! req.fail(function() {! // do something if the request failed! });! ! var lis = $(“li”);! req.done(function(response) {! // do more stuff with response and lis! })!
  9. Javascript community current state •  Node introduced the default api

    of: func(args…,callback(err,arrgs…))! ! •  Some libraries are handling the implementations of Promises •  They got into an agreement called Promises/ A+ –  http://promises-aplus.github.io/promises-spec/
  10. Promises/A+   •  General – A promise represents a value that

    may not be available yet. The primary method for interacting with a promise is its then method.
  11. Some context on Promises utilities •  We are going to

    show some code use the Q library https://github.com/kriskowal/q •  Q.nfbind :: (err -> result -> ()) -> Promise result! •  Q.all :: [Promise] -> Promise [Promise]! •  Q.spread :: [Promise] -> Promise args… -> Promise! •  This is not the exactly type signature, nor Haskell syntax, it is just similar!
  12. Some context on the code var render = console.log;! !

    var handleError = function(error) {! console.log("error");! console.log(error);! } ! ! var User = {};! User.find = function(id, callback) {! setTimeout(function() { ! "callback(Math.random() < 0.1, {id: id}); ! }, 1000);! }!
  13. var Fight = {};! Fight.calculateResult = function(user1, user2, callback) {!

    setTimeout(function() {! "callback(Math.random() < 0.1, {! " " " " " winner: user1,! " " " " " loser: user2 }) }! ,1000);! }! ! Fight.save = function(fight, callback) {! setTimeout(function() { ! "callback(Math.random() < 0.1, {! " " " " " "id: Math.random() * 100}) }! ,1000);! }!
  14. Callback style render("started")! User.find(1, function(err, user) {! if(err) { handleError(err);

    return; }! ! User.find(2, function(err2, user2) {! if(err2) { handleError(err2); return; }! ! Fight.calculateResult(user, user2, function(err3, fight){! if(err3) { handleError(err3); return; }! ! Fight.save(fight, function(err4, fight2){! if(err4) { handleError(err4); return; }! ! render(fight2);! });! });! });! })! render("ended")! !
  15. Promise style var Q = require("q")! var toPromise = Q.nfbind;!

    ! var userFind = toPromise(User.find);! var fightCalculateResult = toPromise(Fight.calculateResult);! var fightSave = toPromise(Fight.save);! ! render("started")! ! Q.all([userFind(1), userFind(2)])! .spread(fightCalculateResult)! .then(fightSave)! .then(render)! .fail(handleError)! ! render("ended")!
  16. Things I think it is important to say •  Expose

    api’s that uses the community style guide to maximize integration –  Expose func(args…, callback(err, arrgs…))! •  Use Promises internally to make future data interaction and flow more explicit •  Use callbacks when it makes sense (ie: click events…) •  Everything in excess is bad •  Try more functional approaches (: