Sane Async Patterns
Upcoming SlideShare
Loading in...5
×
 

Sane Async Patterns

on

  • 27,555 views

Talk given at HTML5DevConf on April 1, 2013.

Talk given at HTML5DevConf on April 1, 2013.

Statistics

Views

Total Views
27,555
Views on SlideShare
17,313
Embed Views
10,242

Actions

Likes
27
Downloads
90
Comments
3

18 Embeds 10,242

http://html5devconf.com 8507
http://www.html5devconf.com 683
http://www.scoop.it 359
http://www.redditmedia.com 228
http://lanyrd.com 118
http://dev.liranuna.com 105
https://twitter.com 79
http://richarrdg.tumblr.com 68
http://www.moarplay.com 42
http://moarplay.com 20
http://hn.dinopost.com 12
http://portal.accelrys.net 10
http://webmelt-test-us-w1.prod.rockmelt.com 3
http://translate.googleusercontent.com 3
https://abs.twimg.com 2
http://webcache.googleusercontent.com 1
http://dev.hubspot.com 1
http://ec2-50-112-12-154.us-west-2.compute.amazonaws.com 1
More...

Accessibility

Categories

Upload Details

Uploaded via as Adobe PDF

Usage Rights

CC Attribution License

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
  • _love_ 'What is PubSub?'
    Are you sure you want to
    Your message goes here
    Processing…
  • What I meant is that there is no single, ubiquitous standard. If an unfamiliar project's documentation says 'This function returns a Promise,' you don't know how to talk to it. If your project uses both jQuery Promises and Promises/A+ Promises, there's going to be confusion. Promises/A+ is a fine standard; unfortunately, it's not the only one.
    Are you sure you want to
    Your message goes here
    Processing…
  • On slide 36, you mention there is no standard for Promises. However, Promises/A+, a community developed standard, has a well-written specification and multiple interoperable implementations, including RSVP in Ember.js, Q, and When.js. The spec and list of implementations may be found at https://github.com/promises-aplus/promises-spec
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

Sane Async Patterns Sane Async Patterns Presentation Transcript

  • Sane Async Patterns Trevor Burnham HTML5DevConf 2013
  • http://dev.hubspot.com/jobs
  • https://pragprog.com/books/tbcoffee
  • https://pragprog.com/books/tbajs
  • https://leanpub.com/npm
  • In This Talk• Callback arguments considered harmful• Three alternative patterns: • PubSub • Promises • AMD
  • The Callback Argument Anti-Pattern
  • Pyramid of DoommainWindow.menu("File", function(err, file) {  if(err) throw err;  file.openMenu(function(err, menu) {    if(err) throw err;    menu.item("Open", function(err, item) {      if(err) throw err;      item.click(function(err) {        if(err) throw err;        window.createDialog(DOOM!, function(err, dialog) {          if(err) throw err;          ...        });      });    });  });});
  • A JS-er’s Lament// Synchronous version of previous slidetry { var file = mainWindow.menu("File");  var menu = file.openMenu();  var item = menu.item("Open");  item.click()  window.createDialog(DOOM!);} catch (err) { ...}
  • A Silver LiningmyFunction1();// No state changes here!myFunction2();// Which means we never have to do this...while (!document.ready) { Thread.sleep(0);}
  • Mo’ Threads...
  • Nested SpaghettimainWindow.menu("File", function(err, file) {  if(err) throw err;  file.openMenu(function(err, menu) {    if(err) throw err;    menu.item("Open", function(err, item) {      if(err) throw err;      item.click(function(err) {        if(err) throw err;        window.createDialog(DOOM!, function(err, dialog) {          if(err) throw err;          ...        });      });    });  });});
  • Inflexible APIsfunction launchRocketAt(target, callback) { var rocket = {x: 0, y: 0}, step = 0; function moveRocket() { rocket.x += target.x * (step / 10); rocket.y += target.y * (step / 10); drawSprite(rocket); if (step === 10) { callback(); } else { step += 1; setTimeout(moveRocket, 50); } } moveRocket();}
  • Inflexible APIslaunchRocketAt(target, function() { // OK, so the rocket reached its target...});
  • Pattern I: PubSub
  • What is PubSub?button.on("click", function(event) { ...});server.on("request", function(req, res, next) { ...});model.on("change", function() { ...});
  • What is PubSub for?• Just about everything!• When in doubt, use PubSub
  • How to use it?• Pick a PubSub library, such as https://github.com/Wolfy87/EventEmitter• If you’re on Node, you already have one• Simply make your objects inherit from EventEmitter, and trigger events on them
  • An Evented RocketRocket.prototype.launchAt = function(target) { rocket = this; _.extend(rocket, {x: 0, y: 0, step: 0}); function moveRocket() { // Physics calculations go here... if (rocket.step === 10) { rocket.emit(complete, rocket); } else { rock.step += 1; setTimeout(moveRocket, 50); } rocket.emit(moved, rocket); } rocket.emit(launched, rocket); moveRocket(); return this;}
  • An Evented Rocketvar rocket = new Rocket();rocket.launchAt(target).on(complete, function() { // Now it’s obvious what this callback is!});
  • PubSub Drawbacks• No standard • Consider using LucidJS: https://github.com/RobertWHurst/ LucidJS
  • Pattern II: Promises
  • What is a Promise?• “A promise represents the eventual value returned from the single completion of an operation.” —The Promises/A Spec
  • What is a Promise?• An object that emits an event when an async task completes (or fails) Resolved Pending Rejected
  • Example 1: Ajaxvar fetchingData = $.get(myData);fetchingData.done(onSuccess);fetchingData.fail(onFailure);fetchingData.state(); // pending// Additional listeners can be added at any timefetchingData.done(celebrate);// `then` is syntactic sugar for done + failfetchingData.then(huzzah, alas);
  • Example 2: Effects$(#header).fadeTo(fast, 0.5).slideUp(fast);$(#content).fadeIn(slow);var animating = $(#header, #content).promise();animating.done(function() { // All of the animations started when promise() // was called are now complete.});
  • What is a Promise?• “A promise is a container for an as-yet- unknown value, and then’s job is to extract the value out of the promise” http://blog.jcoglan.com/2013/03/30/ callbacks-are-imperative-promises-are- functional-nodes-biggest-missed- opportunity/
  • Making Promises// A Promise is a read-only copy of a Deferredvar deferred = $.Deferred();asyncRead(function(err, data) { if (err) { deferred.reject(); } else { deferred.resolve(data); };});var Promise = deferred.promise();
  • Without Promises$.fn.loadAndShowContent(function(options) { var $el = this; function successHandler(content) { $el.html(content); options.success(content); } function errorHandler(err) { $el.html(Error); options.failure(err); } $.ajax(options.url, { success: successHandler, error: errorHandler });});
  • With Promises$.fn.loadAndShowContent(function(options) { var $el = this, fetchingContent = $.ajax(options.url); fetchingContent.done(function(content) { $el.html(content); }); fetchingContent.fail(function(content) { $el.html(Error); }); return fetchingContent;});
  • Merging Promisesvar fetchingData = $.get(myData);var fadingButton = $button.fadeOut().promise();$.when(fetchingData, fadingButton) .then(function() { // Both Promises have resolved});
  • Piping Promisesvar fetchingPassword = $.get(/password);fetchingPassword.done(function(password) { var loggingIn = $.post(/login, password);});// I wish I could attach listeners to the loggingIn// Promise here... but it doesn’t exist yet!
  • Piping Promisesvar fetchingPassword = $.get(/password);var loggingIn = fetchingPassword.pipe(function(password) { return $.post(/login, password);});loggingIn.then(function() { // We’ve logged in successfully}, function(err) { // Either the login failed, or the password fetch failed});// NOTE: As of jQuery 1.8, then and pipe are synonymous.// Use `then` for piping if possible.
  • Piping Promisesvar menuFilePromise = mainWindow.menu(file);var openFilePromise = menuFilePromise.pipe(function(file) {  return file.openMenu();});var menuOpenPromise = openFilePromise.pipe(function(menu) {  return menu.item(open);});var itemClickPromise = menuOpenPromise.pipe(function(item) {  return item.click()});var createDialogPromise = itemClickPromise.pipe(function() {  return window.createDialog("Promises rock!");});
  • A Promise-y Rocketfunction launchRocketAt(target) { var rocketDeferred = $.Deferred(); _.extend(rocketDeferred, {x: 0, y: 0, step: 0}); function moveRocket() { // Physics calculations go here... rocketDeferred.notify(step / 10); if (rocketDeferred.step === 10) { rocketDeferred.resolve(); } else { rocketDeferred.step += 1; setTimeout(moveRocket, 50); } } moveRocket(); return rocketDeferred;}
  • Promise Drawbacks• No standard • jQuery, Promises/A, Promises/B...• For maximum benefit, you’ll need wrappers all over the place
  • Pattern III: AMD
  • What is AMD?• Asynchronous Module Definition, a spec• Each module says which modules it needs• The module’s “factory” is called after all of those modules are loaded
  • What is AMD for?• Loading dependencies as needed• Dependency injection (for tests)• Gating features
  • How to use AMDdefine(myModule, [jQuery, Backbone],function($, Backbone) { var myModule = { // Define some things... }; // If anyone requires this module, they get this object return myModule;});
  • AMD Drawbacks• No standard• Lots of up-front work• No semantic versioning• Heavyweight tools (RequireJS)
  • Alternatives to AMD• Browserify • Simple syntax: require(./filename); • Great if you’re into Node + npm • Intended for bundling, not so much for async module loading
  • Conclusion• The next time you’re about to define a function with a callback argument... don’t.
  • Thanks. Questions? @trevorburnham