Promises generatorscallbacks

Post on 15-Jan-2015

256 views 0 download

Tags:

description

Many developers new to Node.js struggle with writing asynchronous code in a clean, concise, easy to maintain manner. There are many strategies for managing and preventing 'callback hell' in Node.js. We'll walk through many of those strategies, from Promises to Generators to flow control libraries, and show that with a bit of forethought, writing asynchronous code in Javascript and Node.js can be easy and maintainable.

Transcript of Promises generatorscallbacks

Promises, Generators &

Callbacks! Oh my!

Writing asynchronous code is hard

asynchronous code is difficult to read

asynchronous code is difficult to write

asynchronous code is difficult to maintain

Mike Frey

Why is writing asynchronous

code hard?

Composition

Patterns

Callbacks

Promises

Generators

Now entering Nerd War territory

Goals

Callbacks vs

Promises vs

Generators

Callbacks vs

Promises vs

Generators

xx

No Winner

Unbiased

Informative

Callbacks

How do they work?

Continuation Passing

Pass a function

to another function

Ask for work nowHandle result later

askForWork(function(err, res) { // handle result later})

Where are they used?

Everywhere

Node.js core

User-land

Even Promises

Benefits

Simple. Easy to use.

Prolific. Fast.

Problems

Error Handling

try{} catch(){}

try{} catch(){}

try { doWork(function(res) { // handle result })}catch (err) { // handle error }

!

doWork(function(err, res) { // handle error // handle result })!

!

Homework! read this:

!

joyent.com/developers/node/design/errors

ZA L GO Z

Ả L Ğ O !

asynchronous or

synchronous never both

Fix your API: process.nextTick()

setImmediate()

Fix their API: dezalgo

Callback Hell

Composition problem

NowLater

NowLaterLater-er

NowLaterLater-erLater-er-er

NowLaterLater-erLater-er-erLater-er-er-er

There’s nothing forcing you to write ten levels of nested callbacks, but the pattern does make it easy for you to do so.

- Raymond Julin (paraphrased)

NowLaterLater-erLater-er-erLater-er-er-er

NowLaterLater-erLater-er-erLater-er-er-er

getA(function() { getB(function() { getC(function() { // do something }) })})

function handleA() { getB(handleB)}function handleB() { getC(handleC)}function handleC() { // do something}getA(handleA)

async module

async.waterfall([ getA, getB, getC ], function(err, result) { // do something })

Callbacks !

Simple. Everywhere. Be careful.

Promises

How do they work?

Eventual Result

.then()

var promise = doSomething()promise.then( function (result) { // success callback }, function (error) { // error callback })

NowLater successLater failure

Where are they used?

jQuery AngularJS

Ember User-land

Chrome 32 Firefox 29 Opera 19

Node.js 0.11.13

Benefits

Composition: Chaining &

Error handling

.then()

.then()

.then()

NowLater successLater-er successLater-er-er successLater-er-er-er success

function addOne(num) { return new Promise( function(resolve, reject) { resolve(num+1) })}!

addOne(0) .then(addOne) .then(addOne) .then(addOne) .then(console.log)

function addOne(num) { return num+1}!

var p = new Promise( function(res, rej) { res(0) }) .then(addOne) .then(addOne) .then(addOne) .then(console.log)

Rejections bubble

Errors bubble

NowLater successLater-er successLater-er-er successAny failure

getSpeakers('MidwestJS') .then(getGithubUsers) .then(getPublicRepos) .then(listRepos, handleError)

Problems

Slow

Slow

Incompatible Proposals &

Implementations

jQuery vs

everyone else

Promises !

Composable. Eventual Result.

Generators

What are they?

How do they work?

function*

function* tick() {!

!

}!

!

!

!

function* tick() {!

!

}!

var itr = tick()!

!

function* tick() {!

!

}!

var itr = tick()itr.next()!

function* tick() { yield 42!

}!

var itr = tick()itr.next().value // 42!

yield

function* tick() { yield 42 yield 43}!

var itr = tick()itr.next().value // 42itr.next().value // 43itr.next().done // true

function* tick() { var x = yield var y = yield}!

var itr = tick()itr.next()itr.next(42) // x becomes 42itr.next(43) // y becomes 43

function* tick() { var num = 0 while (!(yield num++));}!

var itr = tick()itr.next().value // 0itr.next().value // 1itr.next().value // 2

function* tick() { var num = 0 while (!(yield num++));}!

var itr = tick()itr.next().value // 0itr.next().value // 1itr.next(true).done // true

Replacing callbacks

function delay(time, callback) { setTimeout(function() { callback('Slept for ' + time) }, time)}

function delayThings() { delay(1000, function(result1) { console.log(result1) delay(1200, function(result2) { console.log(result2) }) })}// Slept for 1000// Slept for 1200

function* delayThings() { var results1 = yield delay(1000) console.log(results1)!

var results2 = yield delay(1200) console.log(results2)}

function run(generator) { function resume(value) { itr.next(value) } var itr = generator(resume) itr.next()}

function* delayThings() { var results1 = yield delay(1000) console.log(results1)!

var results2 = yield delay(1200) console.log(results2)}!

function* delayThings(resume) { var results1 = yield delay(1000, resume) console.log(results1)!

var results2 = yield delay(1200, resume) console.log(results2)}!

run(delayThings)

More callbacks?

thunkify

// simplified from// https://github.com/visionmedia/node-thunkify

!function thunkify(fn){ return function(){ var args = Array.prototype.slice.call(arguments) return function(done){ args.push(function(){ done.apply(null, arguments) }) fn.apply(null, args) } }}

delay = thunkify(delay)!

var thunk = delay(1000)!

thunk(function(result) { console.log(result)})

function run(generator) { function resume(value) { itr.next(value) } var itr = generator(resume) itr.next()}!

function run(generator) { function resume(ret) { var obj = itr.next(ret) if (obj.done) return obj.value(resume) } var itr = generator() resume()}

function* delayThings() { var results1 = yield delay(1000) console.log(results1)!

var results2 = yield delay(1200) console.log(results2)}!

run(delayThings)

yield Now Later

yield Now yield Later Later-er

yield Now yield Later yield Later-er Later-er-er

yield Now yield Later yield Later-er yield Later-er-er Later-er-er-er …

co

var delayThings = co(function*() { var results1 = yield delay(1000) console.log(results1)!

var results2 = yield delay(1200) console.log(results2)})!

delayThings()

var delayThings = co(function*() { var delays = [delay(1000), delay(1200)] var results = yield delays!

console.log(results[0]) console.log(results[1])})!

delayThings()

Where are they used?

co koa

User-land

Firefox 31 Chrome (flag)

Node.js v0.11.10 (flag) Opera (flag)

Benefits

Feels synchronous

try{} catch(){}

New cool

Problems

Support

regenerator facebook.github.io/regenerator/

traceur github.com/google/traceur-compiler

Immature implementations

Generators !

yield statement. Pausable function*.

Built for iterators.

Writing asynchronous code is hard

But it doesn’t

have to be!

Call to action

Explore

Streams http://nodestreams.com

http://highlandjs.org

Thank you!

Questions? !

References available here: github.com/mikefrey/cpg-talk