javascript promises/q library

28
Javascript Promises/Q library Jonathan Altman node.dc March 2013 @async_io http://async.io/

Upload: asyncio

Post on 06-May-2015

15.817 views

Category:

Technology


0 download

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

Page 1: Javascript Promises/Q Library

Javascript Promises/Q libraryJonathan Altman

node.dc March 2013@async_io

http://async.io/

Page 2: Javascript Promises/Q Library

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

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

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

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

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

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

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

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

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

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

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

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

Promises can (mostly) be shared across libraries

Page 15: Javascript Promises/Q Library

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

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

Generating Promises With Q

Page 18: Javascript Promises/Q Library

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

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

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

Control Flow and Error Handling

Page 22: Javascript Promises/Q Library

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

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

Testing

Page 25: Javascript Promises/Q Library

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

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

Resources

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

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

Page 28: Javascript Promises/Q Library

Thank you. Questions?