Dead-Simple Async Control Flow with Coroutines
-
Upload
travis-kaufman -
Category
Engineering
-
view
791 -
download
2
Transcript of Dead-Simple Async Control Flow with Coroutines
Dead-Simple Async
Control Flow with
Coroutines
by Travis Kaufman (@traviskaufman)
github.com/traviskaufman/co-talk-examples
OutlineDefine the problem
Async control flow in NodeJS
Present the solutionCoroutines (surprise!)
Explore the implementationHow coroutines work under the hood
Coroutine librariesStart using them in your apps today!
(time permitting) Advanced usageSample code showing where coroutines really shine
source: https://onlivetest.files.wordpress.com/2014/06/pyramid-of-doom.jpg
The Problem
Callbacks are UNINTUITIVE
source: http://i.imgur.com/DEg3cPZ.png
Callbacks are UNINTUITIVE
Simple Program: Get GH Username, print out 10 most recently pushed repos
https://github.com/traviskaufman/co-talk-examples/blob/master/01-raw-callbacks.js
Callbacks are UNINTUITIVE
Simple Program: Get GH Username, print out 10 most recently pushed repos
https://github.com/traviskaufman/co-talk-examples/blob/master/01-raw-callbacks.js
Code duplication
Callbacks are UNINTUITIVE
Simple Program: Get GH Username, print out 10 most recently pushed repos
https://github.com/traviskaufman/co-talk-examples/blob/master/01-raw-callbacks.js
Code duplication
“Diagonal Development” / Pyramid of Doom / Callback Hell / etc
Callbacks are UNINTUITIVE
Simple Program: Get GH Username, print out 10 most recently pushed repos
https://github.com/traviskaufman/co-talk-examples/blob/master/01-raw-callbacks.js
Code duplication
Refactoring callbacks means
jumping around code to see logical
flow of execution :((
“Diagonal Development” / Pyramid of Doom / Callback Hell / etc
https://github.com/traviskaufman/co-talk-examples/blob/master/02-async-lib-callbacks.js
Callbacks are UNINTUITIVEThe “solution”: use a library (e.g. caolan/async, a.k.a. the jQuery of NodeJS)
Callbacks are UNINTUITIVEThe “solution”: use a library (e.g. caolan/async, a.k.a. the jQuery of NodeJS)
Only have to handle this in one place! :D
https://github.com/traviskaufman/co-talk-examples/blob/master/02-async-lib-callbacks.js
Callbacks are UNINTUITIVEThe “solution”: use a library (e.g. caolan/async, a.k.a. the jQuery of NodeJS)
Need to be familiar with “async.waterfall”
API Change to accommodate lib usage?
Only have to handle this in one place! :D
https://github.com/traviskaufman/co-talk-examples/blob/master/02-async-lib-callbacks.js
Promises to the Rescue!
https://github.com/traviskaufman/co-talk-examples/blob/master/03-raw-promises.js
No triangles/pyramids/hells/doom/etc.
Error handling separated from general logic
Promises to the Rescue! but...
https://github.com/traviskaufman/co-talk-examples/blob/master/03-raw-promises.js
No triangles/pyramids/hells/doom/etc.
Error handling separated from general logic
API Weirdness still...
Still requires handler functions for promise settling
Can We Do Better?
Readability of synchronous code
I/O Efficiency of Asynchronous Code
YES! Introducing Coroutines
https://github.com/traviskaufman/co-talk-examples/blob/master/04-co-promises.js
YES! Introducing Coroutines
● coroutine(gen: GeneratorFunction) => Promise
● generator function yields Promises
● values from resolved promises are assigned
● errors from rejected promises are thrown within generator○ note: could use try/catch inside generator to call handleError()
● Returns Promise for easy interop with other async code
● Best of both worlds! Readability/Intuitiveness + Efficiency/Non-blocking I/O
https://github.com/traviskaufman/co-talk-examples/blob/master/04-co-promises.js
F*CK YEAHsource:
http://3.bp.blogspot.com/-
IYYJ3t1g8hA/ToJtrJegK4I/AA
AAAAAAAUg/qTG1Jh50PsY/s
1600/1264744915828.jpg
Coroutines = “Cooperative Routines”
Cooperative threads are
responsible for explicitly telling
executor when they should be
suspended
Differs from preemptive threads
which rely on executor to
suspend/resume them
Reference (Java-based):
http://www.cafeaulait.org/course/
week11/32.html
https://www.cs.mtu.edu/~shene/NSF-3/e-Book/FUNDAMENTALS/thread-yield.jpg
Coroutines = “Cooperative Routines”
Generators provide cooperation
through the yield keyword
Explicitly tells IO-Loop (uv,
etc.) to suspend execution
Promises provide the routines
Scheduled and run
asynchronously by IO-Loop
Generators + Promises =
Cooperation + Routines =
Coroutines!
https://www.cs.mtu.edu/~shene/NSF-3/e-Book/FUNDAMENTALS/thread-yield.jpg
yield
new Promise(...)
A Simple Implementation
< 25 SLOC!
Coroutine Libraries: tj/co
My personal favorite
Supports thunks as well as promises
Supports yielding arrays/objects
(Promise.race), as well as generators
(delegation)
https://github.com/tj/co
Coroutine Libraries: mozilla/task.js
Similar to co
Very powerful built-in scheduler
Adds cancellation to promises
Makes coroutines look a lot like thread-based
scheduling
http://taskjs.org/
“But wait, I need ES2015 for this!”
Use babelJS (https://babeljs.io/) to transpile
your code!Use the --harmony-generators flag with
v0.11+ to enable generators (or just --
harmony)
Check out petkaantonov/bluebird for an
awesome promise polyfill/superset.
Advanced Example: “Retry” with exponential backoff
Advanced Example: DB Cursoring
Additional Resources
MDN reference for generator functions
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function*
Awesome Promise tutorial from HTML5Rocks
http://www.html5rocks.com/en/tutorials/es6/promises/
“Promises and Generators: Control Flow Utopia” - Great presentation by Forbes Lindesay
http://pag.forbeslindesay.co.uk/#/
“No Promises: Asynchronous Javascript with only generators”
http://www.2ality.com/2015/03/no-promises.html
Axel Rauschmayer takes it one step further and shows how you can use generators to write
async code without the need for promises. Super interesting stuff!
THANK YOU SO MUCH! :D