Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.
The strange world of
javascript and
all its little
asynchronous beasts
Federico Galassi
@federicogalassi
http://federico.galassi.net
I wrote this
http://www.jsbestpractices.it
asynchronous
non-blocking
event-driven
confused!
callbacks
...
Rob Pike
Concurrency
is not
Parallelism
http://vimeo.com/49718712
Rob Pike
Concurrency is a way to
structure a program by
breaking it into pieces
that can be executed
independently
Javascript
is
concurrent !
All in one thread
No it’s not.
Joe Armstrong
I want to disappear
Unlike utopian worlds...
I live in
synchronous
bliss!!
Javascript
can learn from
concurrent
languages
Go has gophers
Gophers block on
channels
c := make(chan int)
go func() {
for i := 0; i < 100; i++ {
c <- i
}
}()
go func() {
for {
i := <...
Erlang has actors
Actors block on
receive
P = fun Producer(N, C) when N < 100 ->
C ! {N, self()}, Producer(N + 1, C)
end.
C = fun Consumer()...
The gold rule is
You must be able to
BLOCK
Javascript
was
concurrent
by
necessity
Brendan Eich
Key pressed
Do nothing Button.onclickExec.
Event
queue
Time
Key pressed
the Event Loop
ClickKey pressed
Click
User click
b...
Continuation
passing style
refuel()
startEngine()
takeOff()
land()
// ... done
Continuation
passing style
refuel(function() {
startEngine()
takeOff()
land()
// ... done
})
Continuation
passing style
refuel(function() {
startEngine(function() {
takeOff()
land()
// ... done
})
})
Continuation
passing style
refuel(function() {
startEngine(function() {
takeOff(function() {
land()
// ... done
})
})
})
The Pyramid of Doom
refuel(function() {
startEngine(function() {
takeOff(function() {
land(function() {
// ... done
})
})
...
The Pyramid of Doom
Loss of control flow
images.forEach(function(url) {
var image = download(url)
image.show()
})
Loss of control flow
images.forEach(function(url) {
download(url, function(image) {
image.show()
})
})
Loss of control flow
var showImages = function(images, callback) {
var url = images.shift()
if (url) {
download(url, funct...
Loss of control flow
Loss of error handling
try {
download(url, function(image) {
image.show()
})
} catch(e) {
// never executed!!
console.log(...
Sync/Async Ambiguity
try {
// is download Asynchronous?!?!
download(url, function(image) {
image.show()
})
} catch(e) {
//...
Sync/Async Ambiguity
try {
// Is download Asynchronous?!?!
download(url, function(image) {
image.show()
})
} catch(e) {
//...
It’s not really like this
refuel(function() {
startEngine(function() {
takeOff(function() {
land(function() {
// ... done
...
refuel(function() {
startEngine(function() {
takeOff(function() {
land(function() {
// ... done
})
})
})
})
refuel(functio...
refuel(function() {
startEngine(function() {
takeOff(function() {
land(function() {
// ... done
})
})
})
})
refuel(functio...
What do we know?
When an async call completes
I’m time warped back in time
to the callback code then back
to wherever I came from.
I find t...
It’s even worse, every javascript
programmer who has a concurrent
problem to solve must invent their
own concurrency model...
In javascript you
CAN’T BLOCK
ES6 to rescue us!
with Generators
function* upTo(end) {
for (var i = 0; i <= end; i++) {
yield i
}
}
Generators make
iterators
var counter = upTo(100)
counter.next() // => Object {value: 0, done: false}
counter.next() // =>...
function* upTo(end) {
for (var i = 0; i <= end; i++) {
yield i
}
}
Generators remember
execution stack
restarts
Here!
Yield
can receive values
function* upTo(end) {
for (var i = 0; i <= end; i++) {
var newI = yield i
if (newI) i = newI
}
}
...
Yield
can receive errors
function* upTo(end) {
for (var i = 0; i <= end; i++) {
yield i
}
}
var counter = upTo(100)
counte...
Yes, this is
Blocking!!
Blocking for
sequence
async(function*() {
yield refuel()
yield startEngine()
yield takeOff()
yield land()
})
Blocking for
control flow
async(function*() {
images.forEach(function(url) {
var image = yield download(url)
image.show()
...
Blocking for
error handling
async(function*() {
try {
var image = yield download(url)
image.show()
} catch(e) {
console.lo...
What is async() ?
https://github.com/kriskowal/q/tree/v1/examples/async-generators
http://pag.forbeslindesay.co.uk/#/22
//...
async is too complex
Generators support
http://kangax.github.io/compat-table/es6/#Generators_(yield)
I think this is the way
or there is the dark way
inventing your
own concurrency
model
Joe Armstrong
it will be leaky
Still you can build
your little paradise
Functional composition
with Async.js
https://github.com/caolan/async
Caolan
Mcmahon
functions in Async.js
function(callback) {
download(url, function() {
callback()
})
}
error handling the
node.js way
function(callback) {
download(url, {
success: function(result) {
// success, null error the...
The Pyramid of Doom
refuel(function() {
startEngine(function() {
takeOff(function() {
land(function() {
// ... done
})
})
...
Demolished with
sequential composition
async.series([
refuel,
startEngine,
takeOff,
land
], function(err, result) {
// don...
Pretty control flow
images.forEach(function(url) {
download(url, function(image) {
image.show()
})
})
Pretty
functional composition
async.map(images, function(callback) {
download(url, function(image) {
callback(null, image)...
Composition of
composition
async.waterfall([
function(callback) {
async.map(files, fs.readFile, callback)
},
function(cont...
Composition of
composition is the
real power
Functional
lack of state is the
weakness
No decoupling
async.series([
refuel,
startEngine,
takeOff,
land
], function(err, result) {
// done!
})
call start
callback...
async.series([
refuel,
startEngine,
takeOff,
land
], function(err, result) {
// done!
share = result
})
global
variable
to...
async.series([
refuel,
startEngine,
takeOff,
land
], function(err, result) {
// done!
logger.log(err)
stats.update(result)...
No decoupling
is bad
We need first class
asynchronous calls
to keep the state
of the computation
OOP composition
with Promises
Promise is an object
representing an
async computation
var promise = new Promise(function(resolve, reject) {
// do asynchr...
The computation will
eventually produce
an outcome
var promise = new Promise(function(resolve, reject) {
// do asynchronou...
Promise has
states
var promise = new Promise(function(resolve, reject) {
// do asynchronous computation ...
if (succeeded)...
Promise is thenable
promise.then(
function(result) {
// promise fulfilled
},
function(err) {
// promise rejected
}
)
Promise remembers
its final state
promise
// after some time ...
.then(function(result) {
// fulfilled, result is "hello"
...
Promise remembers
its final state
promise
// after some time ...
.then(function(result) {
// fulfilled, result is "hello"
...
Thenable are
chainable
promise
.then(function() { /* do something */ })
.then(function() { /* do something */ })
.then(fun...
Sequential
composition
promise
.then(refuel)
.then(startEngine)
.then(takeOff)
.then(land)
Pyramid demolished
promise
.then(refuel)
.then(startEngine)
.then(takeOff)
.then(land)
Promisify
function after(time) {
return new Promise(function(resolve) {
setTimeout(resolve, time)
})
}
after(5000)
.then(f...
Promise propagation
promise
.then(function() { /* called immediately */ })
.then(function() { /* called immediately */ })
Promise propagation
promise
.then(function() { return after(5000) })
// returns a promise
.then(function() { /* called aft...
Promise propagation
promise
.then(function() { return 5 })
// returns a value
.then(function(result) { /* result == 5 */ })
Promise propagation
promise
.then(after(5000))
.then(function() { throw new Error("argh") })
// throws an error
.then(func...
Lovely error handling
promise
.then(refuel)
.then(startEngine)
.then(takeOff)
.then(land)
.catch(function(err) {
// deal w...
More composition
var one = after(1000).then(function() { return 1 })
var two = after(2000).then(function() { return 2 })
/...
Stateful joy
Promises have
limited vision
Promises have
limited vision
setInterval(function() {
// does success/error make sense?
// what about next intervals ??
},...
Promises have
limited vision
$button.on("click", function() {
// does success/error make sense?
// what about next events ...
Streams are weird
beasts to promises
Reactive
programming
https://github.com/Reactive-Extensions/RxJS
Eric Meijer
Reactive
programming
Observables are
collections over time
Iterators are pull
iterator.next() // => value
iterator.next() // => value
iterator.next() // => value
// it can return an...
observable.subscribe(
function(value) {
// next value
},
function(err) {
// failure
},
function() {
// completed
}
)
Obser...
Observables can do
setInterval
interval(1000).subscribe(
function(value) {
// value == undefined
// value == undefined
// ...
Observables can do
events
click("#button").subscribe(
function(value) {
// value == { x: 458, y: 788 }
// value == { x: 49...
Observables can do
async calls
download(url).subscribe(
function(image) {
// once!
// image == Image object
},
function(er...
Observables are
all-around
OOP structure
functional composition
var mouseup = Rx.Observable.fromEvent(dragTarget, 'mouseup')
var mousemove = Rx.Obser...
Powerful model
Used at scale by
Netflix
https://github.com/Netflix/RxJava/wiki
Take Away
Block
if you can
Choose the
concurrency
model that
fits you
@federicogalassi
Upcoming SlideShare
Loading in …5
×

The Strange World of Javascript and all its little Asynchronous Beasts

1,967 views

Published on

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

Published in: Technology, Business
  • Be the first to comment

The Strange World of Javascript and all its little Asynchronous Beasts

  1. 1. The strange world of javascript and all its little asynchronous beasts
  2. 2. Federico Galassi @federicogalassi http://federico.galassi.net
  3. 3. I wrote this http://www.jsbestpractices.it
  4. 4. asynchronous non-blocking event-driven confused! callbacks ...
  5. 5. Rob Pike Concurrency is not Parallelism http://vimeo.com/49718712
  6. 6. Rob Pike Concurrency is a way to structure a program by breaking it into pieces that can be executed independently
  7. 7. Javascript is concurrent !
  8. 8. All in one thread No it’s not. Joe Armstrong
  9. 9. I want to disappear
  10. 10. Unlike utopian worlds... I live in synchronous bliss!!
  11. 11. Javascript can learn from concurrent languages
  12. 12. Go has gophers
  13. 13. Gophers block on channels c := make(chan int) go func() { for i := 0; i < 100; i++ { c <- i } }() go func() { for { i := <- c fmt.Println(i) } }()
  14. 14. Erlang has actors
  15. 15. Actors block on receive 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() end end. Cons = spawn(C). P(0, Cons).
  16. 16. The gold rule is
  17. 17. You must be able to BLOCK
  18. 18. Javascript was concurrent by necessity Brendan Eich
  19. 19. Key pressed Do nothing Button.onclickExec. Event queue Time Key pressed the Event Loop ClickKey pressed Click User click button.onclick = function() { element.style.color = "red" })
  20. 20. Continuation passing style refuel() startEngine() takeOff() land() // ... done
  21. 21. Continuation passing style refuel(function() { startEngine() takeOff() land() // ... done })
  22. 22. Continuation passing style refuel(function() { startEngine(function() { takeOff() land() // ... done }) })
  23. 23. Continuation passing style refuel(function() { startEngine(function() { takeOff(function() { land() // ... done }) }) })
  24. 24. The Pyramid of Doom refuel(function() { startEngine(function() { takeOff(function() { land(function() { // ... done }) }) }) })
  25. 25. The Pyramid of Doom
  26. 26. Loss of control flow images.forEach(function(url) { var image = download(url) image.show() })
  27. 27. Loss of control flow images.forEach(function(url) { download(url, function(image) { image.show() }) })
  28. 28. 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() } })
  29. 29. Loss of control flow
  30. 30. Loss of error handling try { download(url, function(image) { image.show() }) } catch(e) { // never executed!! console.log("Cannot show image") }
  31. 31. Sync/Async Ambiguity try { // is download Asynchronous?!?! download(url, function(image) { image.show() }) } catch(e) { // never executed!! console.log("Cannot show image") }
  32. 32. Sync/Async Ambiguity try { // Is download Asynchronous?!?! download(url, function(image) { image.show() }) } catch(e) { // never executed!! console.log("Cannot show image") }
  33. 33. It’s not really like this refuel(function() { startEngine(function() { takeOff(function() { land(function() { // ... done }) }) }) })
  34. 34. 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 ...
  35. 35. 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 ... ??? ??? ???
  36. 36. What do we know?
  37. 37. When an async call completes I’m time warped back in time to the callback code then back to wherever I came from. I find this very difficult to understand http://joearms.github.io/2013/04/02/Red-and-Green-Callbacks.html Joe Armstrong
  38. 38. 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
  39. 39. In javascript you CAN’T BLOCK
  40. 40. ES6 to rescue us!
  41. 41. with Generators function* upTo(end) { for (var i = 0; i <= end; i++) { yield i } }
  42. 42. 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}
  43. 43. function* upTo(end) { for (var i = 0; i <= end; i++) { yield i } } Generators remember execution stack restarts Here!
  44. 44. Yield can 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}
  45. 45. Yield can 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
  46. 46. Yes, this is Blocking!!
  47. 47. Blocking for sequence async(function*() { yield refuel() yield startEngine() yield takeOff() yield land() })
  48. 48. Blocking for control flow async(function*() { images.forEach(function(url) { var image = yield download(url) image.show() }) })
  49. 49. Blocking for error handling async(function*() { try { var image = yield download(url) image.show() } catch(e) { console.log("Cannot show image") } })
  50. 50. What is async() ? https://github.com/kriskowal/q/tree/v1/examples/async-generators http://pag.forbeslindesay.co.uk/#/22 // Implementation by Lindesay Forbes function 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()) } }
  51. 51. async is too complex
  52. 52. Generators support http://kangax.github.io/compat-table/es6/#Generators_(yield)
  53. 53. I think this is the way
  54. 54. or there is the dark way
  55. 55. inventing your own concurrency model Joe Armstrong
  56. 56. it will be leaky
  57. 57. Still you can build your little paradise
  58. 58. Functional composition with Async.js https://github.com/caolan/async Caolan Mcmahon
  59. 59. functions in Async.js function(callback) { download(url, function() { callback() }) }
  60. 60. 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) } }) }
  61. 61. The Pyramid of Doom refuel(function() { startEngine(function() { takeOff(function() { land(function() { // ... done }) }) }) })
  62. 62. Demolished with sequential composition async.series([ refuel, startEngine, takeOff, land ], function(err, result) { // done! })
  63. 63. Pretty control flow images.forEach(function(url) { download(url, function(image) { image.show() }) })
  64. 64. Pretty functional composition async.map(images, function(callback) { download(url, function(image) { callback(null, image) }) }, function(err, results) { results.forEach(function(image) { image.show() }) })
  65. 65. 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 })
  66. 66. Composition of composition is the real power
  67. 67. Functional lack of state is the weakness
  68. 68. No decoupling async.series([ refuel, startEngine, takeOff, land ], function(err, result) { // done! }) call start callback binding
  69. 69. async.series([ refuel, startEngine, takeOff, land ], function(err, result) { // done! share = result }) global variable to share No decoupling
  70. 70. async.series([ refuel, startEngine, takeOff, land ], function(err, result) { // done! logger.log(err) stats.update(result) spinner.hide() }) divergent change No decoupling
  71. 71. No decoupling is bad
  72. 72. We need first class asynchronous calls to keep the state of the computation
  73. 73. OOP composition with Promises
  74. 74. Promise is an object representing 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/
  75. 75. 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/
  76. 76. Promise has states var promise = new Promise(function(resolve, reject) { // do asynchronous computation ... if (succeeded) { resolve(result) } else { reject(new Error(err)) } }) pending fulfilled rejected
  77. 77. Promise is thenable promise.then( function(result) { // promise fulfilled }, function(err) { // promise rejected } )
  78. 78. 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" })
  79. 79. 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" })
  80. 80. Thenable are chainable promise .then(function() { /* do something */ }) .then(function() { /* do something */ }) .then(function() { /* do something */ }) .then(function() { /* do something */ })
  81. 81. Sequential composition promise .then(refuel) .then(startEngine) .then(takeOff) .then(land)
  82. 82. Pyramid demolished promise .then(refuel) .then(startEngine) .then(takeOff) .then(land)
  83. 83. Promisify function after(time) { return new Promise(function(resolve) { setTimeout(resolve, time) }) } after(5000) .then(function() { // five seconds gone! })
  84. 84. Promise propagation promise .then(function() { /* called immediately */ }) .then(function() { /* called immediately */ })
  85. 85. Promise propagation promise .then(function() { return after(5000) }) // returns a promise .then(function() { /* called after 5 secs */ }) .then(function() { /* called after 5 secs */ })
  86. 86. Promise propagation promise .then(function() { return 5 }) // returns a value .then(function(result) { /* result == 5 */ })
  87. 87. Promise propagation promise .then(after(5000)) .then(function() { throw new Error("argh") }) // throws an error .then(function() { /* never called */ }) .then(null, function(err) { // err == Error(“argh”) })
  88. 88. Lovely error handling promise .then(refuel) .then(startEngine) .then(takeOff) .then(land) .catch(function(err) { // deal with err })
  89. 89. More composition var one = after(1000).then(function() { return 1 }) var two = after(2000).then(function() { return 2 }) // parallel, wait for all Promise.all([one, two]).then(function(result) { // after 2 seconds // result == [1, 2] }) // parallel, wins the first Promise.race([one, two]).then(function(result) { // after 1 second // result == 1 })
  90. 90. Stateful joy
  91. 91. Promises have limited vision
  92. 92. Promises have limited vision setInterval(function() { // does success/error make sense? // what about next intervals ?? }, 1000)
  93. 93. Promises have limited vision $button.on("click", function() { // does success/error make sense? // what about next events ?? })
  94. 94. Streams are weird beasts to promises
  95. 95. Reactive programming https://github.com/Reactive-Extensions/RxJS
  96. 96. Eric Meijer Reactive programming
  97. 97. Observables are collections over time
  98. 98. Iterators are pull iterator.next() // => value iterator.next() // => value iterator.next() // => value // it can return an error iterator.next() // => Error("argh") // it can end iterator.hasNext() // => false iterator.next() // => null
  99. 99. observable.subscribe( function(value) { // next value }, function(err) { // failure }, function() { // completed } ) Observables are push
  100. 100. Observables can do setInterval interval(1000).subscribe( function(value) { // value == undefined // value == undefined // ... }, function(err) { // never called }, function() { // when the interval is cleared } )
  101. 101. Observables can do events click("#button").subscribe( function(value) { // value == { x: 458, y: 788 } // value == { x: 492, y: 971 } // ... }, function(err) { // never called }, function() { // when click is unsubscribed } )
  102. 102. Observables can do async calls download(url).subscribe( function(image) { // once! // image == Image object }, function(err) { // once! // if error }, function() { // once! // when either succeeded or failed }
  103. 103. Observables are all-around
  104. 104. OOP structure functional composition var 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 position var subscription = mousedrag.subscribe(function (pos) { dragTarget.style.top = pos.top + 'px' dragTarget.style.left = pos.left + 'px' })
  105. 105. Powerful model
  106. 106. Used at scale by Netflix https://github.com/Netflix/RxJava/wiki
  107. 107. Take Away
  108. 108. Block if you can
  109. 109. Choose the concurrency model that fits you
  110. 110. @federicogalassi

×