Sane Async Patterns

  • 27,056 views
Uploaded on

Talk given at HTML5DevConf on April 1, 2013.

Talk given at HTML5DevConf on April 1, 2013.

More in: Technology
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
  • _love_ 'What is PubSub?'
    Are you sure you want to
    Your message goes here
  • 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
  • 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
No Downloads

Views

Total Views
27,056
On Slideshare
0
From Embeds
0
Number of Embeds
14

Actions

Shares
Downloads
92
Comments
3
Likes
27

Embeds 0

No embeds

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
    No notes for slide

Transcript

  • 1. Sane Async Patterns Trevor Burnham HTML5DevConf 2013
  • 2. http://dev.hubspot.com/jobs
  • 3. https://pragprog.com/books/tbcoffee
  • 4. https://pragprog.com/books/tbajs
  • 5. https://leanpub.com/npm
  • 6. In This Talk• Callback arguments considered harmful• Three alternative patterns: • PubSub • Promises • AMD
  • 7. The Callback Argument Anti-Pattern
  • 8. 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;          ...        });      });    });  });});
  • 9. 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) { ...}
  • 10. A Silver LiningmyFunction1();// No state changes here!myFunction2();// Which means we never have to do this...while (!document.ready) { Thread.sleep(0);}
  • 11. Mo’ Threads...
  • 12. 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;          ...        });      });    });  });});
  • 13. 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();}
  • 14. Inflexible APIslaunchRocketAt(target, function() { // OK, so the rocket reached its target...});
  • 15. Pattern I: PubSub
  • 16. What is PubSub?button.on("click", function(event) { ...});server.on("request", function(req, res, next) { ...});model.on("change", function() { ...});
  • 17. What is PubSub for?• Just about everything!• When in doubt, use PubSub
  • 18. 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
  • 19. 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;}
  • 20. An Evented Rocketvar rocket = new Rocket();rocket.launchAt(target).on(complete, function() { // Now it’s obvious what this callback is!});
  • 21. PubSub Drawbacks• No standard • Consider using LucidJS: https://github.com/RobertWHurst/ LucidJS
  • 22. Pattern II: Promises
  • 23. What is a Promise?• “A promise represents the eventual value returned from the single completion of an operation.” —The Promises/A Spec
  • 24. What is a Promise?• An object that emits an event when an async task completes (or fails) Resolved Pending Rejected
  • 25. 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);
  • 26. 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.});
  • 27. 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/
  • 28. 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();
  • 29. 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 });});
  • 30. 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;});
  • 31. Merging Promisesvar fetchingData = $.get(myData);var fadingButton = $button.fadeOut().promise();$.when(fetchingData, fadingButton) .then(function() { // Both Promises have resolved});
  • 32. 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!
  • 33. 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.
  • 34. 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!");});
  • 35. 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;}
  • 36. Promise Drawbacks• No standard • jQuery, Promises/A, Promises/B...• For maximum benefit, you’ll need wrappers all over the place
  • 37. Pattern III: AMD
  • 38. 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
  • 39. What is AMD for?• Loading dependencies as needed• Dependency injection (for tests)• Gating features
  • 40. 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;});
  • 41. AMD Drawbacks• No standard• Lots of up-front work• No semantic versioning• Heavyweight tools (RequireJS)
  • 42. 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
  • 43. Conclusion• The next time you’re about to define a function with a callback argument... don’t.
  • 44. Thanks. Questions? @trevorburnham