javascript promises/q library
DESCRIPTION
Presentation I gave to the node.dc meetup group March 13, 2013 on using Promises and the Q library to make flow of control easier to reason about in Javascript code using async and callbacksTRANSCRIPT
![Page 1: Javascript Promises/Q Library](https://reader035.vdocuments.us/reader035/viewer/2022081801/554a0e4eb4c90507558b4a38/html5/thumbnails/1.jpg)
Javascript Promises/Q libraryJonathan Altman
node.dc March 2013@async_io
http://async.io/
![Page 2: Javascript Promises/Q Library](https://reader035.vdocuments.us/reader035/viewer/2022081801/554a0e4eb4c90507558b4a38/html5/thumbnails/2.jpg)
What is a Promise? The simplest explanation: it is an easy way to avoid writing the Pyramid of Doom
![Page 3: Javascript Promises/Q Library](https://reader035.vdocuments.us/reader035/viewer/2022081801/554a0e4eb4c90507558b4a38/html5/thumbnails/3.jpg)
Pyramid of Doomstep1(function (value1) {
step2(value1, function(value2) {
step3(value2, function(value3) {
step4(value3, function(value4) {
// Do something with value4
});
});
});
}); // from https://github.com/kriskowal/q
![Page 4: Javascript Promises/Q Library](https://reader035.vdocuments.us/reader035/viewer/2022081801/554a0e4eb4c90507558b4a38/html5/thumbnails/4.jpg)
Why Promises? What about?
• async: http://github.com/caolan/async
• step: https://github.com/creationix/step
• flow-js: https://github.com/willconant/flow-js
• ...you get the point
![Page 5: Javascript Promises/Q Library](https://reader035.vdocuments.us/reader035/viewer/2022081801/554a0e4eb4c90507558b4a38/html5/thumbnails/5.jpg)
Non-Promises Fix:async.series({
normalize: function(callback){// Bodies removed for brevity
}, compose: function(callback){}, invert: function(callback){}// A bunch more stuff killed
}, function (err, results){if (!err ) {err = new Error('Results did not contain a valid image buffer')
}else {callback(err, results.imageBuffer);
}}
}); //https://github.com/jonathana/heatNode/blob/master/lib/imageGen/generateimages.js
![Page 6: Javascript Promises/Q Library](https://reader035.vdocuments.us/reader035/viewer/2022081801/554a0e4eb4c90507558b4a38/html5/thumbnails/6.jpg)
Is That Not Good Enough?var later = Q.nfcall(nodedc.wait_a_few_slides);
// or: we’ll come back to it
![Page 7: Javascript Promises/Q Library](https://reader035.vdocuments.us/reader035/viewer/2022081801/554a0e4eb4c90507558b4a38/html5/thumbnails/7.jpg)
Promises: Longer Explanation“A Promise is an object representation of an event. In the course of its life, a Promise goes from a pending state, when it’s called, to a resolved or rejected state, when it’s been completed, or it could also stay pending forever and is never resolved.”
http://flaviocopes.com/deferred-and-promises-in-javascript/
![Page 8: Javascript Promises/Q Library](https://reader035.vdocuments.us/reader035/viewer/2022081801/554a0e4eb4c90507558b4a38/html5/thumbnails/8.jpg)
Say What?
• Promises take a call to an asynchronous function and wrap it with an object whose methods proxy when the wrapped function either completes or errors
• A good Promise library also provides a set of control methods on that object wrapper to handle composition of multiple Promise-ified asynchronous method calls
• Promises use the best qualities of an object--encapsulation of state--to track the state of an asynchronous call
![Page 9: Javascript Promises/Q Library](https://reader035.vdocuments.us/reader035/viewer/2022081801/554a0e4eb4c90507558b4a38/html5/thumbnails/9.jpg)
Promises provide asolid abstractionfor representing the
state of an asynchronous calland writing flow of
control code based on that state
![Page 10: Javascript Promises/Q Library](https://reader035.vdocuments.us/reader035/viewer/2022081801/554a0e4eb4c90507558b4a38/html5/thumbnails/10.jpg)
Pyramid of Doom Againstep1(function (value1) {
step2(value1, function(value2) {
step3(value2, function(value3) {
step4(value3, function(value4) {
// Do something with value4
});
});
});
}); // from https://github.com/kriskowal/q
![Page 11: Javascript Promises/Q Library](https://reader035.vdocuments.us/reader035/viewer/2022081801/554a0e4eb4c90507558b4a38/html5/thumbnails/11.jpg)
Pyramid of Doom on PromisesQ.fcall(step1) // This returns a Promise obj.then(step2).then(step3).then(step4).then(function (value4) { // Do something with value4}, function (error) { // Handle any error from step1 through step4}).done(); // from https://github.com/kriskowal/q
![Page 12: Javascript Promises/Q Library](https://reader035.vdocuments.us/reader035/viewer/2022081801/554a0e4eb4c90507558b4a38/html5/thumbnails/12.jpg)
Again, Why Promises?
• It’s a spec: http://wiki.commonjs.org/wiki/Promises/A
• Generally supported by a bunch of libs both browser and server-side:
• jQuery (sort of, supposedly doesn’t fully work like Promises/A)
• AngularJS
• Q library (https://github.com/kriskowal/q)
• Provides separation of concerns between wrapping and flow of control handling of deferred activities
![Page 13: Javascript Promises/Q Library](https://reader035.vdocuments.us/reader035/viewer/2022081801/554a0e4eb4c90507558b4a38/html5/thumbnails/13.jpg)
later.then(function(){Provides separation of concerns between wrapping and flow of
control handling of deferred activities
// that separation is the key
});
![Page 14: Javascript Promises/Q Library](https://reader035.vdocuments.us/reader035/viewer/2022081801/554a0e4eb4c90507558b4a38/html5/thumbnails/14.jpg)
Promises can (mostly) be shared across libraries
![Page 15: Javascript Promises/Q Library](https://reader035.vdocuments.us/reader035/viewer/2022081801/554a0e4eb4c90507558b4a38/html5/thumbnails/15.jpg)
Sharing
• Many libraries can exchange Promise objects
• AngularJS’ Promise API is explicitly based off a subset of Q. If Q is loads first, AngularJS just uses that
• jQuery’s promises can be consumed by Q, with some adaptations
![Page 16: Javascript Promises/Q Library](https://reader035.vdocuments.us/reader035/viewer/2022081801/554a0e4eb4c90507558b4a38/html5/thumbnails/16.jpg)
So Why Q?
• Q consumes Promises from most other libraries
• Pure Javascript library
• Can be used both client-side (browser or e.g. Phonegap) and server-side (npm install q, but you’re using package.json, right?)
• Provides utilities to make it easy to write Promise-based code or wrap non-Promise-based functions
• Provides a library of methods to build control flow around Promise results and errors
• Small (enough?): ~1400 SLOC, ~ 8.5kb minified
![Page 17: Javascript Promises/Q Library](https://reader035.vdocuments.us/reader035/viewer/2022081801/554a0e4eb4c90507558b4a38/html5/thumbnails/17.jpg)
Generating Promises With Q
![Page 18: Javascript Promises/Q Library](https://reader035.vdocuments.us/reader035/viewer/2022081801/554a0e4eb4c90507558b4a38/html5/thumbnails/18.jpg)
Q.fcall/nfcall: Wrap an Async method with a Promise
function writeError(errMessage) { return Q.nfcall(fs.writeFile, "errors.log", errMessage);}• nfcall: node function call. Sort of a misnomer, original intent was to
make it easy to wrap node library/module calls into Promises, but works for any async call
• fcall: turns functions synchronously returning a value into a Promise, • You can make it so it’s Promises all the way down (at least until you hit
the turtles)
![Page 19: Javascript Promises/Q Library](https://reader035.vdocuments.us/reader035/viewer/2022081801/554a0e4eb4c90507558b4a38/html5/thumbnails/19.jpg)
Q.defer: Interject Promise Support• Use when you have to intermingle other logic inside callback work
function getLocation() { var deferred = Q.defer(); console.log("Calling getCurrentPosition"); navigator.geolocation.getCurrentPosition(function(position) { deferred.resolve(position); console.log("getCurrentPosition resolved"); }, function(error){ deferred.reject(error); console.log("getCurrentPosition errored"); }); return deferred.promise; };
![Page 20: Javascript Promises/Q Library](https://reader035.vdocuments.us/reader035/viewer/2022081801/554a0e4eb4c90507558b4a38/html5/thumbnails/20.jpg)
Q.when: Wrapping Other Libraries’ Promises
• From the Q documentation: “Not all promise libraries make the same guarantees as Q and certainly don’t provide all of the same methods. Most libraries only provide a partially functional then method.”
return Q.when($.ajax(...)).then(function () {});
![Page 21: Javascript Promises/Q Library](https://reader035.vdocuments.us/reader035/viewer/2022081801/554a0e4eb4c90507558b4a38/html5/thumbnails/21.jpg)
Control Flow and Error Handling
![Page 22: Javascript Promises/Q Library](https://reader035.vdocuments.us/reader035/viewer/2022081801/554a0e4eb4c90507558b4a38/html5/thumbnails/22.jpg)
Simple Flow• Use then/fail (and .done() to avoid swallowing unhandled exceptions)
• You can chain then calls
Q.fcall(step1) // This returns a Promise obj.then(step2).then(step3).then(step4).then(function (value4) { // Do something with value4}, function (error) { // Handle any error from step1 through step4}).done(); // from https://github.com/kriskowal/q
![Page 23: Javascript Promises/Q Library](https://reader035.vdocuments.us/reader035/viewer/2022081801/554a0e4eb4c90507558b4a38/html5/thumbnails/23.jpg)
More Complex Handling
• Q.reduce(): Chain indeterminate-length sequential chains
• all(): Turn multiple Promises in an array into a single Promise. Fails at the first failure from any Promise, returning that failure
• allResolved(): Turn multiple Promises in an array into a single Promise. Succeeds when all Promises complete, resolved or failed, and resolves with the array of Promises
![Page 24: Javascript Promises/Q Library](https://reader035.vdocuments.us/reader035/viewer/2022081801/554a0e4eb4c90507558b4a38/html5/thumbnails/24.jpg)
Testing
![Page 25: Javascript Promises/Q Library](https://reader035.vdocuments.us/reader035/viewer/2022081801/554a0e4eb4c90507558b4a38/html5/thumbnails/25.jpg)
Mocha + Chai + Chai-as-Promised
• Mocha: http://visionmedia.github.com/mocha/ -- unit testing framework
• Chai: http://chaijs.com/ -- BDD add-on for Mocha
• Chai-as-promised: https://github.com/domenic/chai-as-promised -- Promises-enables Chai/Mocha
![Page 26: Javascript Promises/Q Library](https://reader035.vdocuments.us/reader035/viewer/2022081801/554a0e4eb4c90507558b4a38/html5/thumbnails/26.jpg)
Mocha + Chai + Chai-as-Promised
• Adds testability methods to promises supporting BDD
• Note the use of done, which signals the async part of mocha
it('Should error on short barcode format', function(done){ var promise = lookupBarcode(lookupData.shortGtinData.gtin); promise.should.be.rejected.and.notify(done);});
![Page 27: Javascript Promises/Q Library](https://reader035.vdocuments.us/reader035/viewer/2022081801/554a0e4eb4c90507558b4a38/html5/thumbnails/27.jpg)
Resources
• Q javascript library: https://github.com/kriskowal/q
• Q documentation: http://documentup.com/kriskowal/q/
![Page 28: Javascript Promises/Q Library](https://reader035.vdocuments.us/reader035/viewer/2022081801/554a0e4eb4c90507558b4a38/html5/thumbnails/28.jpg)
Thank you. Questions?