javascript promises/q library

Post on 06-May-2015

15.829 Views

Category:

Technology

0 Downloads

Preview:

Click to see full reader

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 callbacks

TRANSCRIPT

Javascript Promises/Q libraryJonathan Altman

node.dc March 2013@async_io

http://async.io/

What is a Promise? The simplest explanation: it is an easy way to avoid writing the Pyramid of Doom

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

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

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

Is That Not Good Enough?var later = Q.nfcall(nodedc.wait_a_few_slides);

// or: we’ll come back to it

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/

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

Promises provide asolid abstractionfor representing the

state of an asynchronous calland writing flow of

control code based on that state

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

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

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

later.then(function(){Provides separation of concerns between wrapping and flow of

control handling of deferred activities

// that separation is the key

});

Promises can (mostly) be shared across libraries

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

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

Generating Promises With Q

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)

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; };

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 () {});

Control Flow and Error Handling

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

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

Testing

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

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);});

Resources

• Q javascript library: https://github.com/kriskowal/q

• Q documentation: http://documentup.com/kriskowal/q/

Thank you. Questions?

top related