Promises, Promises: Mastering Async I/O in Javascript with the Promise Pattern

52
Promises, Promises Mastering Async in Javascript with the Promise Pattern by Christian Lilley about.me/xml Thursday, September 19, 13

Transcript of Promises, Promises: Mastering Async I/O in Javascript with the Promise Pattern

Page 1: Promises, Promises: Mastering Async I/O in Javascript with the Promise Pattern

Promises, PromisesMastering Async in Javascript

with the Promise Pattern

by Christian Lilleyabout.me/xml

Thursday, September 19, 13

Page 2: Promises, Promises: Mastering Async I/O in Javascript with the Promise Pattern

1st, our Problem...Do Thing A.

Do Thing B using output of Thing A.

But Thing A hasn’t returned yet, & you don’t know when it will!!!

Thursday, September 19, 13

Page 3: Promises, Promises: Mastering Async I/O in Javascript with the Promise Pattern

Then, the *Name*YMMV. Ignore the name if you need to.

also: ‘Futures’, ‘Deferrables’, et al.

Some of those are misleading...

But still pithier than my ‘Time-Independent Proxy Objects’

TIPOs!

Better: ‘Timeless Values’, ‘IOUs’

Thursday, September 19, 13

Page 4: Promises, Promises: Mastering Async I/O in Javascript with the Promise Pattern

The Nature of JS

Thursday, September 19, 13

Page 5: Promises, Promises: Mastering Async I/O in Javascript with the Promise Pattern

Thursday, September 19, 13

Page 6: Promises, Promises: Mastering Async I/O in Javascript with the Promise Pattern

The Nature of JS(Terminology on this stuff varies: smarter people than me argue about ‘blocking’ vs. ‘non-blocking’, etc.)

FWIW, JS doesn’t have ‘real’ concurrency or coroutines

‘Just’ a Single-Threaded Event Loop

Which actually works really well, once you understand it.

Hence, Node’s doing pretty well.

Thursday, September 19, 13

Page 7: Promises, Promises: Mastering Async I/O in Javascript with the Promise Pattern

The Default OptionSo, by default, Async in JS is all about callbacks.

Callbacks get an otherwise blocking long-lived function out of the loop. They’re the exception to sequential, ‘blocking’ execution.

Callbacks don’t return values, they have *side-effects*

If you miss them, they’re gone forever

Thursday, September 19, 13

Page 8: Promises, Promises: Mastering Async I/O in Javascript with the Promise Pattern

Callback RealitySo, callbacks work, but they’re a pain to compose and think about: tons of manual control-flow, custom event systems, caching of process states...

People hate callbacks so much, they come up with colorful names for what they make their code look like:

“Callback Hell”, or...

“Callback Spaghetti”, or...

Thursday, September 19, 13

Page 9: Promises, Promises: Mastering Async I/O in Javascript with the Promise Pattern

step1(value1,  function  (output)  {        step2(output1,  function(output2)  {                step3(output2,  function(output3)  {                        step4(output3,  function(output4)  {                                //  Do  something  with  value4,

       quick,  before  it                                  disappears!!!

                       });                });        });});

Thursday, September 19, 13

Page 10: Promises, Promises: Mastering Async I/O in Javascript with the Promise Pattern

PYRAMID OF DOOOM!!!!

Thursday, September 19, 13

Page 11: Promises, Promises: Mastering Async I/O in Javascript with the Promise Pattern

Doom is bad.

Thursday, September 19, 13

Page 12: Promises, Promises: Mastering Async I/O in Javascript with the Promise Pattern

Instead, how about...

Thursday, September 19, 13

Page 13: Promises, Promises: Mastering Async I/O in Javascript with the Promise Pattern

Q.fcall(step1).then(step2).then(step3).then(step4);

Nice! Although, what if we don’t want to chain everything at once?...

Thursday, September 19, 13

Page 14: Promises, Promises: Mastering Async I/O in Javascript with the Promise Pattern

var myPromise = Q.fcall(step1);

// later...var newPromise = myPromise.then(step2);

// much later...newPromise.then(step3).then(step4);

// and some other time entirely...myPromise.then(step5)

Thursday, September 19, 13

Page 15: Promises, Promises: Mastering Async I/O in Javascript with the Promise Pattern

Or, Aggregation!

Thursday, September 19, 13

Page 16: Promises, Promises: Mastering Async I/O in Javascript with the Promise Pattern

// First, do one thing:var 1stPromise = Q.fcall(step1);

// After that, do 3 other thingsvar 2ndPromise = myPromise.then(step2);

var 3rdPromise = myPromise.then(step3);

var 4thPromise = myPromise.then(step4);

// And only after those 3 finish...Q.all([2ndPromise, 3rdPromise, 4thPromise]).then(step5)

Thursday, September 19, 13

Page 17: Promises, Promises: Mastering Async I/O in Javascript with the Promise Pattern

So, what’s going on?Q.js is a utility library for generating promises. There are others, but Q is the gold-standard, and fully compliant with Promises/A+ spec.

Subset of Q is in Angular: $Q

JQuery has an implementation: avoid

In the past, folks used Async.js, other utilities, for similar purposes.

Thursday, September 19, 13

Page 18: Promises, Promises: Mastering Async I/O in Javascript with the Promise Pattern

But, what’s a Promise?!?

A promise is something very simple, but very powerful:

a container that holds a value, or will hold a value

a time-independent proxy for a remote/external object

an instance of a class that has utility methods like .then()

Thursday, September 19, 13

Page 19: Promises, Promises: Mastering Async I/O in Javascript with the Promise Pattern

But, what’s a Promise?!?

A ‘frozen event’ you can learn status of at any time.

It’s a first-class object, so you can:

Pass itReference itChain it

All these things, in ONE PACKAGE

Thursday, September 19, 13

Page 20: Promises, Promises: Mastering Async I/O in Javascript with the Promise Pattern

Metaphors:

Thursday, September 19, 13

Page 21: Promises, Promises: Mastering Async I/O in Javascript with the Promise Pattern

Mailbox/Loading-DockA package containing a new drive is coming. Will be delivered to this spot, but not sure when.

var newDriveReady = orderDrive().then(openBox).then(copyData)

var oldDriveGone = newDriveReady.then(wipeDrive).then(sellDrive)

var newDriveInstalled = newDriveReady.then(installDrive)

Q.all([newDriveInstalled, oldDriveGone]).done(downloadMoreMusic)

Thursday, September 19, 13

Page 22: Promises, Promises: Mastering Async I/O in Javascript with the Promise Pattern

Family MessagingRather than standing waiting for your kid at school, & for spouse at work, make plans & do other stuff.

var kidPromise = kidToSchool();

var childHome = kidPromise.then(smsReceived).then(pickUpKid);

var foodAvailable = kidPromise.then(goShopping);

var spouseHome = spouseToWork().then(vmReceived).then(pickUpSpouse);

Q.all([childHome, spouseHome, foodAvailable]).then(makeDinner).done(eatDinner);

Thursday, September 19, 13

Page 23: Promises, Promises: Mastering Async I/O in Javascript with the Promise Pattern

MoneyThe callback approach to life is that you get paid for your work in perishable goods, like food.

You have to preserve or sell it to be able to store it.

Wouldn’t you rather just get paid in money, which you can spend, save, or even take out loans against?

Thursday, September 19, 13

Page 24: Promises, Promises: Mastering Async I/O in Javascript with the Promise Pattern

So, yes: it’s very nice syntactic sugar for composing more readable interactions, especially with callback-based applications.

But that’s missing the bigger points:

Persistent Events

Cached Values from I/O operations

Error-Handling

Take async out of controllers, etc., w/ patterns like Angular’s

Thursday, September 19, 13

Page 25: Promises, Promises: Mastering Async I/O in Javascript with the Promise Pattern

Infinitely chainable: Promise methods return a promise, either transformed or original.

Aggregation, with .all

Furthermore...

Thursday, September 19, 13

Page 26: Promises, Promises: Mastering Async I/O in Javascript with the Promise Pattern

How-To

Thursday, September 19, 13

Page 27: Promises, Promises: Mastering Async I/O in Javascript with the Promise Pattern

Consuming At first, consume promises from Angular's $HTTP, similar methods that return promises that you've maybe been ignoring all along.

Inspect the state of a promise with:

promise.inspect() (returns state & value or reason)

promise.isPending()

promise.isFulfilled()

promise.isRejected()

Thursday, September 19, 13

Page 28: Promises, Promises: Mastering Async I/O in Javascript with the Promise Pattern

Consuming But once you've seen how much easier life gets when you take maximum advantage of Promises, you'll want to not only consume them, but produce your own...

Thursday, September 19, 13

Page 29: Promises, Promises: Mastering Async I/O in Javascript with the Promise Pattern

Producing - Step 1 Start Simple, by wrapping a basic value or function output in a Promise, so you can .then() or .all()

Q.fcall(function() {return 10;});

Q.fcall(calculateSomething(data));

These promises will already be resolved. (They’re not async, right?)

Thursday, September 19, 13

Page 30: Promises, Promises: Mastering Async I/O in Javascript with the Promise Pattern

Producing - Step 2 Take callback-producing functions and turn them easily into promise-producing functions, especially in Node:

var filePromise = Q.nfcall(FS.readFile, "foo.txt", "utf-8");

filePromise.done(handler);

(‘nfcall()’ = node function call, but you can use it elsewhere)

(There’s also nfapply(), of course.)

Thursday, September 19, 13

Page 31: Promises, Promises: Mastering Async I/O in Javascript with the Promise Pattern

Producing - Step 3 If you want to assemble your own promise-generating functions, from-scratch, and resolve or reject when/how you wish, you want...

Thursday, September 19, 13

Page 32: Promises, Promises: Mastering Async I/O in Javascript with the Promise Pattern

Deferreds

Thursday, September 19, 13

Page 33: Promises, Promises: Mastering Async I/O in Javascript with the Promise Pattern

What’s a Deferred?If a Promise is a container for a value, then a Deferred is the custodian of the container, taking care of it until it grows up & leaves home.

The promise is a property of the deferred, which also has special methods for resolving the promise.

So, re-using the previous example:

Thursday, September 19, 13

Page 34: Promises, Promises: Mastering Async I/O in Javascript with the Promise Pattern

var deferred = Q.defer();FS.readFile("foo.txt", "utf-8",

// callback to `readFile()`function (error, text) {

if (error) { deferred.reject(new Error(error)); } else {

deferred.resolve(text); }}

);// returns the promise before the async function completesreturn deferred.promise;

Thursday, September 19, 13

Page 35: Promises, Promises: Mastering Async I/O in Javascript with the Promise Pattern

The Promise is a separate object, to which the

Deferred holds a reference:

Thursday, September 19, 13

Page 36: Promises, Promises: Mastering Async I/O in Javascript with the Promise Pattern

Deferredfor creating,

resolving promises

PromiseUser/Consumer

methods live here.

.resolve()

.reject()

.promise

.then()

.done()

.all()

.inspect()

.state

.value

.catch()

.fcall()

etc...

Thursday, September 19, 13

Page 37: Promises, Promises: Mastering Async I/O in Javascript with the Promise Pattern

Which can do What?You can’t change the state of a Promise directly. Only its Deferred object can do that.

The Deferred is able to resolve or reject the Promise. If you don’t have the Deferred, you’re just a consumer.

Promise is a bit like the compiled binary, Deferred is the source.

Thursday, September 19, 13

Page 38: Promises, Promises: Mastering Async I/O in Javascript with the Promise Pattern

Let’s beat that mailbox metaphor to death, shall we?

Thursday, September 19, 13

Page 39: Promises, Promises: Mastering Async I/O in Javascript with the Promise Pattern

If the Promise is the mailbox, then the Deferred:

Is the Postman.

Thursday, September 19, 13

Page 40: Promises, Promises: Mastering Async I/O in Javascript with the Promise Pattern

No, not that one, thank

God...

Thursday, September 19, 13

Page 41: Promises, Promises: Mastering Async I/O in Javascript with the Promise Pattern

If the Promise is the mailbox, then the Deferred:

Is the Postman.

The Postman is the only one who’s allowed to:

Assign you a new mailbox. (ie. create a promise)Put new packages into the mailbox. (resolve the promise)Raise the little flag on the side. (set state of the promise)

Thursday, September 19, 13

Page 42: Promises, Promises: Mastering Async I/O in Javascript with the Promise Pattern

Recap:

Thursday, September 19, 13

Page 43: Promises, Promises: Mastering Async I/O in Javascript with the Promise Pattern

Promises Get YouClear, Declarative Control-Flow That Works Regardless of Time & Place

Persistent Events, Clear Handlers

Parallelism, Aggregation

Caching of Async Values

Excellent Error-Handling

Thursday, September 19, 13

Page 44: Promises, Promises: Mastering Async I/O in Javascript with the Promise Pattern

Advanced Patterns

Thursday, September 19, 13

Page 45: Promises, Promises: Mastering Async I/O in Javascript with the Promise Pattern

Promises from Other Libraries

Not all promises are Promises/A+ spec

I’m looking at you, JQuery.

Also, some spec libraries are limited

Wrap any other promise in conversion methods like Q(jqueryPromise);

Just like wrapping a bare DOM element in JQuery, so you can use those methods.

Thursday, September 19, 13

Page 46: Promises, Promises: Mastering Async I/O in Javascript with the Promise Pattern

Angular & PromisesAngular shows what you can do when you start thinking in Promises.

Most async methods return promises.

($resource used to be an exception, but that’s changing in 1.2)

Thursday, September 19, 13

Page 47: Promises, Promises: Mastering Async I/O in Javascript with the Promise Pattern

Angular & Promises`resolve` property on routes: always resolved before controller instantiated, view rendered.

allows controllers, views to be fully time-agnostic

more modular, reusable, focused

Make use of a *service* (a persistent singleton) to cache your promises, perhaps using...

Thursday, September 19, 13

Page 48: Promises, Promises: Mastering Async I/O in Javascript with the Promise Pattern

Lazy PromisesUse a simple getter function that will return a promise if it already exists (whether resolved yet, or not), or generate one if needed.

Works well with namespace() for creating hierarchies on the fly.

Promises/A+ considering standardizing

AMD loaders basically built on them

Thursday, September 19, 13

Page 49: Promises, Promises: Mastering Async I/O in Javascript with the Promise Pattern

function getDivision(divisionName) { if (!divisionObjects[divisionName]) { divisionObjects[divisionName] =

$http.get(baseUrl + divisionName).error(function(data, status) {

console.error('getDivision failed, error:' + status) }); } else { return divisionObjects[divisionName]; }}

Thursday, September 19, 13

Page 50: Promises, Promises: Mastering Async I/O in Javascript with the Promise Pattern

NotesAsync.JS - Still useful, but promises are a better overall abstraction, that can change your whole structure.

AMD/Require.JS is async and is a version of promises: *lazy* promises.

I lied before: Web Workers are bringing real concurrency/multi-threading to JS !!! (IE 10 or better) But they won’t make callbacks go away.

Thursday, September 19, 13

Page 51: Promises, Promises: Mastering Async I/O in Javascript with the Promise Pattern

NotesNode is recent enough that they certainly could have used Promises instead of callbacks. And servers are all about I/O. But everybody already understands callbacks...

Robust, readable error-handling is another great feature of Promises.

Thursday, September 19, 13

Page 52: Promises, Promises: Mastering Async I/O in Javascript with the Promise Pattern

ReferencesJohn Resig: How Timers Work (Also generally about the whole JS event-loop)

Trevor Burnham: Flow-Control With Promises

Callbacks are imperative, promises are functional: Node’s biggest missed opportunity

Domenic: You’re Missing the Point of Promises

Trevor Burnham’s Async Javascript (Book @ PragProg)

Writing Asynchronous Javascript 101 (It should actually be a ‘301’, and is dated, but good info)

Thursday, September 19, 13