The document discusses different patterns for handling asynchronous code in JavaScript: callbacks, promises, and AMD (Asynchronous Module Definition). It outlines issues with nested callbacks and inflexible APIs. Promises and AMD aim to address these by allowing composition of asynchronous operations and defining module dependencies. The document provides examples of implementing PubSub with events, making and piping promises, and using AMD to load dependencies asynchronously. It concludes that callbacks should generally be avoided in favor of promises or AMD for asynchronous code.
9. A JS-er’s Lament
// Synchronous version of previous slide
try {
var file = mainWindow.menu("File");
var menu = file.openMenu();
var item = menu.item("Open");
item.click()
window.createDialog('DOOM!');
} catch (err) {
...
}
10. A Silver Lining
myFunction1();
// No state changes here!
myFunction2();
// Which means we never have to do this...
while (!document.ready) {
Thread.sleep(0);
}
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
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: Ajax
var fetchingData = $.get('myData');
fetchingData.done(onSuccess);
fetchingData.fail(onFailure);
fetchingData.state(); // 'pending'
// Additional listeners can be added at any time
fetchingData.done(celebrate);
// `then` is syntactic sugar for done + fail
fetchingData.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 Deferred
var 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
});
});
31. Merging Promises
var fetchingData = $.get('myData');
var fadingButton = $button.fadeOut().promise();
$.when(fetchingData, fadingButton)
.then(function() {
// Both Promises have resolved
});
32. Piping Promises
var 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 Promises
var 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 Promises
var 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 Rocket
function 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
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 AMD
define('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.