110
The strange world of javascript and all its little asynchronous beasts

The Strange World of Javascript and all its little Asynchronous Beasts

Embed Size (px)

DESCRIPTION

Javascript is a wonderland populated by all kinds of exotic beasts. Callbacks, events, promises, functional programming, reactive programming, streams, generators are all part of this incredible asynchronous bestiary. We’re talking of insidious or even ferocious things, a great danger to the unwary programmer. Let’s look at them, learn from them, tame them and finally put them to our own advantage. Let’s stop Javascript from being an unfamiliar place and make it feel much more like home. Talk I held on 14/05/2014 at JsDay, Verona, Italy. Corrected slides. http://2014.jsday.it/talk/the-strange-world-of-javascript-and-all-its-little-asynchronous-beasts/ Feedback! https://joind.in/talk/view/11280 Follow me on Twitter! https://twitter.com/federicogalassi

Citation preview

Page 1: The Strange World of Javascript and all its little Asynchronous Beasts

The strange world of javascript and

all its littleasynchronous beasts

Page 2: The Strange World of Javascript and all its little Asynchronous Beasts

Federico Galassi

@federicogalassi

http://federico.galassi.net

Page 3: The Strange World of Javascript and all its little Asynchronous Beasts

I wrote this

http://www.jsbestpractices.it

Page 4: The Strange World of Javascript and all its little Asynchronous Beasts

asynchronous

non-blocking

event-driven

confused!

callbacks...

Page 5: The Strange World of Javascript and all its little Asynchronous Beasts

Rob Pike

Concurrencyis not

Parallelism

http://vimeo.com/49718712

Page 6: The Strange World of Javascript and all its little Asynchronous Beasts

Rob Pike

Concurrency is a way tostructure a program bybreaking it into piecesthat can be executedindependently

Page 7: The Strange World of Javascript and all its little Asynchronous Beasts

Javascriptis

concurrent !

Page 8: The Strange World of Javascript and all its little Asynchronous Beasts

All in one threadNo it’s not.

Joe Armstrong

Page 9: The Strange World of Javascript and all its little Asynchronous Beasts

I want to disappear

Page 10: The Strange World of Javascript and all its little Asynchronous Beasts

Unlike utopian worlds...I live in

synchronousbliss!!

Page 11: The Strange World of Javascript and all its little Asynchronous Beasts

Javascriptcan learn from

concurrentlanguages

Page 12: The Strange World of Javascript and all its little Asynchronous Beasts

Go has gophers

Page 13: The Strange World of Javascript and all its little Asynchronous Beasts

Gophers block onchannels

c := make(chan int)go func() { for i := 0; i < 100; i++ { c <- i }}()go func() { for { i := <- c fmt.Println(i) }}()

Page 14: The Strange World of Javascript and all its little Asynchronous Beasts

Erlang has actors

Page 15: The Strange World of Javascript and all its little Asynchronous Beasts

Actors block onreceive

P = fun Producer(N, C) when N < 100 -> C ! {N, self()}, Producer(N + 1, C)end.

C = fun Consumer() -> receive {N, Pid} -> io:format("received ~p from ~p~n", [N, Pid]), Consumer() endend.

Cons = spawn(C).P(0, Cons).

Page 16: The Strange World of Javascript and all its little Asynchronous Beasts

The gold rule is

Page 17: The Strange World of Javascript and all its little Asynchronous Beasts

You must be able toBLOCK

Page 18: The Strange World of Javascript and all its little Asynchronous Beasts

Javascriptwas

concurrentby

necessity

Brendan Eich

Page 19: The Strange World of Javascript and all its little Asynchronous Beasts

Key pressed

Do nothing Button.onclickExec.

Eventqueue

TimeKey pressed

the Event Loop

ClickKey pressed

Click

User click

button.onclick = function() { element.style.color = "red"})

Page 20: The Strange World of Javascript and all its little Asynchronous Beasts

Continuationpassing style

refuel()startEngine()takeOff()land()// ... done

Page 21: The Strange World of Javascript and all its little Asynchronous Beasts

Continuationpassing style

refuel(function() { startEngine() takeOff() land() // ... done})

Page 22: The Strange World of Javascript and all its little Asynchronous Beasts

Continuationpassing style

refuel(function() { startEngine(function() { takeOff() land() // ... done })})

Page 23: The Strange World of Javascript and all its little Asynchronous Beasts

Continuationpassing style

refuel(function() { startEngine(function() { takeOff(function() { land() // ... done }) })})

Page 24: The Strange World of Javascript and all its little Asynchronous Beasts

The Pyramid of Doom

refuel(function() { startEngine(function() { takeOff(function() { land(function() { // ... done }) }) })})

Page 25: The Strange World of Javascript and all its little Asynchronous Beasts

The Pyramid of Doom

Page 26: The Strange World of Javascript and all its little Asynchronous Beasts

Loss of control flow

images.forEach(function(url) { var image = download(url) image.show()})

Page 27: The Strange World of Javascript and all its little Asynchronous Beasts

Loss of control flow

images.forEach(function(url) { download(url, function(image) { image.show() })})

Page 28: The Strange World of Javascript and all its little Asynchronous Beasts

Loss of control flow

var showImages = function(images, callback) { var url = images.shift() if (url) { download(url, function(image) { image.show() showImages(images, callback) }) } else { callback() }})

Page 29: The Strange World of Javascript and all its little Asynchronous Beasts

Loss of control flow

Page 30: The Strange World of Javascript and all its little Asynchronous Beasts

Loss of error handling

try { download(url, function(image) { image.show() })} catch(e) { // never executed!! console.log("Cannot show image")}

Page 31: The Strange World of Javascript and all its little Asynchronous Beasts

Sync/Async Ambiguity

try { // is download Asynchronous?!?! download(url, function(image) { image.show() })} catch(e) { // never executed!! console.log("Cannot show image")}

Page 32: The Strange World of Javascript and all its little Asynchronous Beasts

Sync/Async Ambiguity

try { // Is download Asynchronous?!?! download(url, function(image) { image.show() })} catch(e) { // never executed!! console.log("Cannot show image")}

Page 33: The Strange World of Javascript and all its little Asynchronous Beasts

It’s not really like this

refuel(function() { startEngine(function() { takeOff(function() { land(function() { // ... done }) }) })})

Page 34: The Strange World of Javascript and all its little Asynchronous Beasts

refuel(function() { startEngine(function() { takeOff(function() { land(function() { // ... done }) }) })})

refuel(function() { startEngine(function() { takeOff(function() { land(function() { // ... done }) }) })})

refuel(function() { startEngine(function() { takeOff(function() { land(function() { // ... done }) }) })})

refuel(function() { startEngine(function() { takeOff(function() { land(function() { // ... done }) }) })})

More like this

// ... After a while ...

Page 35: The Strange World of Javascript and all its little Asynchronous Beasts

refuel(function() { startEngine(function() { takeOff(function() { land(function() { // ... done }) }) })})

refuel(function() { startEngine(function() { takeOff(function() { land(function() { // ... done }) }) })})

refuel(function() { startEngine(function() { takeOff(function() { land(function() { // ... done }) }) })})

refuel(function() { startEngine(function() { takeOff(function() { land(function() { // ... done }) }) })})

Where do we land?

// ... After a while ...

???

???

???

Page 36: The Strange World of Javascript and all its little Asynchronous Beasts

refuel(function() { startEngine(function() { takeOff(function() { land(function() { // ... done }) }) })})

What do we know?

Page 37: The Strange World of Javascript and all its little Asynchronous Beasts

When an async call completesI’m time warped back in timeto the callback code then backto wherever I came from.I find this very difficult tounderstand

http://joearms.github.io/2013/04/02/Red-and-Green-Callbacks.html

Joe Armstrong

Page 38: The Strange World of Javascript and all its little Asynchronous Beasts

It’s even worse, every javascript programmer who has a concurrent problem to solve must invent their own concurrency model

Joe Armstrong

http://joearms.github.io/2013/04/02/Red-and-Green-Callbacks.html

Page 39: The Strange World of Javascript and all its little Asynchronous Beasts

In javascript youCAN’T BLOCK

Page 40: The Strange World of Javascript and all its little Asynchronous Beasts

ES6 to rescue us!

Page 41: The Strange World of Javascript and all its little Asynchronous Beasts

with Generators

function* upTo(end) { for (var i = 0; i <= end; i++) { yield i }}

Page 42: The Strange World of Javascript and all its little Asynchronous Beasts

Generators make iterators

var counter = upTo(100)

counter.next() // => Object {value: 0, done: false}counter.next() // => Object {value: 1, done: false}counter.next() // => Object {value: 2, done: false}

// ...

counter.next() // => Object {value: 99, done: false}counter.next() // => Object {value: undefined, done: true}

Page 43: The Strange World of Javascript and all its little Asynchronous Beasts

function* upTo(end) { for (var i = 0; i <= end; i++) {

yield i

}}

Generators rememberexecution stack

restartsHere!

Page 44: The Strange World of Javascript and all its little Asynchronous Beasts

Yieldcan receive values

function* upTo(end) { for (var i = 0; i <= end; i++) { var newI = yield i if (newI) i = newI }}

var counter = upTo(100)

counter.next() // => Object {value: 0, done: false}counter.next() // => Object {value: 1, done: false}counter.next(10) // => Object {value: 11, done: false}counter.next() // => Object {value: 12, done: false}

Page 45: The Strange World of Javascript and all its little Asynchronous Beasts

Yieldcan receive errors

function* upTo(end) { for (var i = 0; i <= end; i++) { yield i }}

var counter = upTo(100)

counter.next() // => Object {value: 0, done: false}counter.next() // => Object {value: 1, done: false}counter.throw(new Error("argh")) // => Error: argh

Page 46: The Strange World of Javascript and all its little Asynchronous Beasts

Yes, this isBlocking!!

Page 47: The Strange World of Javascript and all its little Asynchronous Beasts

Blocking for sequence

async(function*() { yield refuel() yield startEngine() yield takeOff() yield land()})

Page 48: The Strange World of Javascript and all its little Asynchronous Beasts

Blocking forcontrol flow

async(function*() { images.forEach(function(url) { var image = yield download(url) image.show() })})

Page 49: The Strange World of Javascript and all its little Asynchronous Beasts

Blocking forerror handling

async(function*() { try { var image = yield download(url) image.show() } catch(e) { console.log("Cannot show image") }})

Page 50: The Strange World of Javascript and all its little Asynchronous Beasts

What is async() ?

https://github.com/kriskowal/q/tree/v1/examples/async-generators

http://pag.forbeslindesay.co.uk/#/22

// Implementation by Lindesay Forbesfunction async(makeGenerator){ return function (){ var generator = makeGenerator.apply(this, arguments) function handle(result){ // { done: [Boolean], value: [Object] } if (result.done) return result.value return result.value.then(function (res){ return handle(generator.next(res)) }, function (err){ return handle(generator.throw(err)) }) } return handle(generator.next()) }}

Page 51: The Strange World of Javascript and all its little Asynchronous Beasts

async is too complex

Page 52: The Strange World of Javascript and all its little Asynchronous Beasts

Generators support

http://kangax.github.io/compat-table/es6/#Generators_(yield)

Page 53: The Strange World of Javascript and all its little Asynchronous Beasts

I think this is the way

Page 54: The Strange World of Javascript and all its little Asynchronous Beasts

or there is the dark way

Page 55: The Strange World of Javascript and all its little Asynchronous Beasts

inventing your own concurrency

modelJoe Armstrong

Page 56: The Strange World of Javascript and all its little Asynchronous Beasts

it will be leaky

Page 57: The Strange World of Javascript and all its little Asynchronous Beasts

Still you can buildyour little paradise

Page 58: The Strange World of Javascript and all its little Asynchronous Beasts

Functional compositionwith Async.js

https://github.com/caolan/async

CaolanMcmahon

Page 59: The Strange World of Javascript and all its little Asynchronous Beasts

functions in Async.js

function(callback) { download(url, function() { callback() })}

Page 60: The Strange World of Javascript and all its little Asynchronous Beasts

error handling the node.js way

function(callback) { download(url, { success: function(result) { // success, null error then result callback(null, result) }, error: function(err) { // failure, error and no result callback(err) } })}

Page 61: The Strange World of Javascript and all its little Asynchronous Beasts

The Pyramid of Doom

refuel(function() { startEngine(function() { takeOff(function() { land(function() { // ... done }) }) })})

Page 62: The Strange World of Javascript and all its little Asynchronous Beasts

Demolished withsequential composition

async.series([ refuel, startEngine, takeOff, land], function(err, result) { // done!})

Page 63: The Strange World of Javascript and all its little Asynchronous Beasts

Pretty control flow

images.forEach(function(url) { download(url, function(image) { image.show() })})

Page 64: The Strange World of Javascript and all its little Asynchronous Beasts

Prettyfunctional composition

async.map(images, function(callback) { download(url, function(image) { callback(null, image) })}, function(err, results) { results.forEach(function(image) { image.show() })})

Page 65: The Strange World of Javascript and all its little Asynchronous Beasts

Composition of composition

async.waterfall([ function(callback) { async.map(files, fs.readFile, callback) }, function(contents, callback) { async.map(contents, countWords, callback) }, function(countedWords, callback) { async.reduce(countedWords, 0, sum, callback) },], function(err, totalWords) { // The number of words in files is totalWords})

Page 66: The Strange World of Javascript and all its little Asynchronous Beasts

Composition of composition is the

real power

Page 67: The Strange World of Javascript and all its little Asynchronous Beasts

Functionallack of state is the

weakness

Page 68: The Strange World of Javascript and all its little Asynchronous Beasts

No decoupling

async.series([ refuel, startEngine, takeOff, land], function(err, result) { // done!})

call start

callbackbinding

Page 69: The Strange World of Javascript and all its little Asynchronous Beasts

async.series([ refuel, startEngine, takeOff, land], function(err, result) { // done! share = result})

globalvariableto share

No decoupling

Page 70: The Strange World of Javascript and all its little Asynchronous Beasts

async.series([ refuel, startEngine, takeOff, land], function(err, result) { // done! logger.log(err) stats.update(result) spinner.hide()})

divergentchange

No decoupling

Page 71: The Strange World of Javascript and all its little Asynchronous Beasts

No decouplingis bad

Page 72: The Strange World of Javascript and all its little Asynchronous Beasts

We need first classasynchronous calls

to keep the stateof the computation

Page 73: The Strange World of Javascript and all its little Asynchronous Beasts

OOP compositionwith Promises

Page 74: The Strange World of Javascript and all its little Asynchronous Beasts

Promise is an objectrepresenting an

async computation

var promise = new Promise(function(resolve, reject) { // do asynchronous computation ... if (succeeded) { resolve(result) } else { reject(new Error(err)) }})

http://promisesaplus.com

https://www.promisejs.org/

Page 75: The Strange World of Javascript and all its little Asynchronous Beasts

The computation will eventually produce

an outcome

var promise = new Promise(function(resolve, reject) { // do asynchronous computation ... if (succeeded) { resolve(result) } else { reject(new Error(err)) }})

http://promisesaplus.com

https://www.promisejs.org/

Page 76: The Strange World of Javascript and all its little Asynchronous Beasts

Promise hasstates

var promise = new Promise(function(resolve, reject) { // do asynchronous computation ... if (succeeded) { resolve(result) } else { reject(new Error(err)) }})

pending

fulfilled

rejected

Page 77: The Strange World of Javascript and all its little Asynchronous Beasts

Promise is thenable

promise.then( function(result) { // promise fulfilled }, function(err) { // promise rejected })

Page 78: The Strange World of Javascript and all its little Asynchronous Beasts

Promise remembers its final state

promise // after some time ... .then(function(result) { // fulfilled, result is "hello" })

// after a while ...

promise .then(function(result) { // fulfilled, result still "hello" })

Page 79: The Strange World of Javascript and all its little Asynchronous Beasts

Promise remembers its final state

promise // after some time ... .then(function(result) { // fulfilled, result is "hello" })

// after a while ...

promise .then(function(result) { // fulfilled, result still "hello" })

Page 80: The Strange World of Javascript and all its little Asynchronous Beasts

Thenable are chainable

promise .then(function() { /* do something */ }) .then(function() { /* do something */ }) .then(function() { /* do something */ }) .then(function() { /* do something */ })

Page 81: The Strange World of Javascript and all its little Asynchronous Beasts

Sequential composition

promise .then(refuel) .then(startEngine) .then(takeOff) .then(land)

Page 82: The Strange World of Javascript and all its little Asynchronous Beasts

Pyramid demolished

promise .then(refuel) .then(startEngine) .then(takeOff) .then(land)

Page 83: The Strange World of Javascript and all its little Asynchronous Beasts

Promisify

function after(time) { return new Promise(function(resolve) { setTimeout(resolve, time) })}

after(5000) .then(function() { // five seconds gone! })

Page 84: The Strange World of Javascript and all its little Asynchronous Beasts

Promise propagation

promise .then(function() { /* called immediately */ }) .then(function() { /* called immediately */ })

Page 85: The Strange World of Javascript and all its little Asynchronous Beasts

Promise propagation

promise .then(function() { return after(5000) })

// returns a promise .then(function() { /* called after 5 secs */ }) .then(function() { /* called after 5 secs */ })

Page 86: The Strange World of Javascript and all its little Asynchronous Beasts

Promise propagation

promise .then(function() { return 5 })

// returns a value .then(function(result) { /* result == 5 */ })

Page 87: The Strange World of Javascript and all its little Asynchronous Beasts

Promise propagationpromise .then(after(5000)) .then(function() { throw new Error("argh") })

// throws an error .then(function() { /* never called */ }) .then(null, function(err) {

// err == Error(“argh”)})

Page 88: The Strange World of Javascript and all its little Asynchronous Beasts

Lovely error handling

promise .then(refuel) .then(startEngine) .then(takeOff) .then(land) .catch(function(err) { // deal with err })

Page 89: The Strange World of Javascript and all its little Asynchronous Beasts

More compositionvar one = after(1000).then(function() { return 1 })var two = after(2000).then(function() { return 2 })

// parallel, wait for allPromise.all([one, two]).then(function(result) { // after 2 seconds // result == [1, 2]})

// parallel, wins the firstPromise.race([one, two]).then(function(result) { // after 1 second // result == 1})

Page 90: The Strange World of Javascript and all its little Asynchronous Beasts

Stateful joy

Page 91: The Strange World of Javascript and all its little Asynchronous Beasts

Promises have limited vision

Page 92: The Strange World of Javascript and all its little Asynchronous Beasts

Promises have limited vision

setInterval(function() { // does success/error make sense? // what about next intervals ??}, 1000)

Page 93: The Strange World of Javascript and all its little Asynchronous Beasts

Promises have limited vision

$button.on("click", function() { // does success/error make sense? // what about next events ??})

Page 94: The Strange World of Javascript and all its little Asynchronous Beasts

Streams are weird beasts to promises

Page 95: The Strange World of Javascript and all its little Asynchronous Beasts

Reactive programming

https://github.com/Reactive-Extensions/RxJS

Page 96: The Strange World of Javascript and all its little Asynchronous Beasts

Eric Meijer

Reactive programming

Page 97: The Strange World of Javascript and all its little Asynchronous Beasts

Observables are collections over time

Page 98: The Strange World of Javascript and all its little Asynchronous Beasts

Iterators are pull

iterator.next() // => valueiterator.next() // => valueiterator.next() // => value

// it can return an erroriterator.next() // => Error("argh")

// it can enditerator.hasNext() // => falseiterator.next() // => null

Page 99: The Strange World of Javascript and all its little Asynchronous Beasts

observable.subscribe( function(value) { // next value }, function(err) { // failure }, function() { // completed })

Observables are push

Page 100: The Strange World of Javascript and all its little Asynchronous Beasts

Observables can dosetInterval

interval(1000).subscribe( function(value) { // value == undefined // value == undefined // ... }, function(err) { // never called }, function() { // when the interval is cleared })

Page 101: The Strange World of Javascript and all its little Asynchronous Beasts

Observables can doevents

click("#button").subscribe( function(value) { // value == { x: 458, y: 788 } // value == { x: 492, y: 971 } // ... }, function(err) { // never called }, function() { // when click is unsubscribed })

Page 102: The Strange World of Javascript and all its little Asynchronous Beasts

Observables can doasync calls

download(url).subscribe( function(image) { // once! // image == Image object }, function(err) { // once! // if error }, function() { // once! // when either succeeded or failed }

Page 103: The Strange World of Javascript and all its little Asynchronous Beasts

Observables areall-around

Page 104: The Strange World of Javascript and all its little Asynchronous Beasts

OOP structurefunctional compositionvar mouseup = Rx.Observable.fromEvent(dragTarget, 'mouseup')var mousemove = Rx.Observable.fromEvent(document, 'mousemove')var mousedown = Rx.Observable.fromEvent(dragTarget, 'mousedown')

var mousedrag = mousedown.selectMany(function (md) {

// calculate offsets when mouse down var startX = md.offsetX, startY = md.offsetY

// Calculate delta with mousemove until mouseup return mousemove.select(function (mm) { return { left: mm.clientX - startX, top: mm.clientY - startY } }).takeUntil(mouseup)})

// Update positionvar subscription = mousedrag.subscribe(function (pos) { dragTarget.style.top = pos.top + 'px' dragTarget.style.left = pos.left + 'px'})

Page 105: The Strange World of Javascript and all its little Asynchronous Beasts

Powerful model

Page 106: The Strange World of Javascript and all its little Asynchronous Beasts

Used at scale byNetflix

https://github.com/Netflix/RxJava/wiki

Page 107: The Strange World of Javascript and all its little Asynchronous Beasts

Take Away

Page 108: The Strange World of Javascript and all its little Asynchronous Beasts

Blockif you can

Page 109: The Strange World of Javascript and all its little Asynchronous Beasts

Choose the concurrencymodel that

fits you

Page 110: The Strange World of Javascript and all its little Asynchronous Beasts

@federicogalassi