Dead-Simple Async Control Flow with Coroutines

27
Dead-Simple Async Control Flow with Coroutines by Travis Kaufman (@traviskaufman) github.com/traviskaufman/co-talk-examples

Transcript of Dead-Simple Async Control Flow with Coroutines

Page 1: Dead-Simple Async Control Flow with Coroutines

Dead-Simple Async

Control Flow with

Coroutines

by Travis Kaufman (@traviskaufman)

github.com/traviskaufman/co-talk-examples

Page 2: Dead-Simple Async Control Flow with Coroutines

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

Page 3: Dead-Simple Async Control Flow with Coroutines

source: https://onlivetest.files.wordpress.com/2014/06/pyramid-of-doom.jpg

The Problem

Page 4: Dead-Simple Async Control Flow with Coroutines

Callbacks are UNINTUITIVE

source: http://i.imgur.com/DEg3cPZ.png

Page 5: Dead-Simple Async Control Flow with Coroutines

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

Page 6: Dead-Simple Async Control Flow with Coroutines

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

Page 7: Dead-Simple Async Control Flow with Coroutines

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

Page 8: Dead-Simple Async Control Flow with Coroutines

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

Page 9: Dead-Simple Async Control Flow with Coroutines

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)

Page 10: Dead-Simple Async Control Flow with Coroutines

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

Page 11: Dead-Simple Async Control Flow with Coroutines

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

Page 12: Dead-Simple Async Control Flow with Coroutines

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

Page 13: Dead-Simple Async Control Flow with Coroutines

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

Page 14: Dead-Simple Async Control Flow with Coroutines

Can We Do Better?

Readability of synchronous code

I/O Efficiency of Asynchronous Code

Page 15: Dead-Simple Async Control Flow with Coroutines

YES! Introducing Coroutines

https://github.com/traviskaufman/co-talk-examples/blob/master/04-co-promises.js

Page 16: Dead-Simple Async Control Flow with Coroutines

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

Page 17: Dead-Simple Async Control Flow with Coroutines

F*CK YEAHsource:

http://3.bp.blogspot.com/-

IYYJ3t1g8hA/ToJtrJegK4I/AA

AAAAAAAUg/qTG1Jh50PsY/s

1600/1264744915828.jpg

Page 18: Dead-Simple Async Control Flow with Coroutines

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

Page 19: Dead-Simple Async Control Flow with Coroutines

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(...)

Page 20: Dead-Simple Async Control Flow with Coroutines

A Simple Implementation

< 25 SLOC!

Page 21: Dead-Simple Async Control Flow with Coroutines

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

Page 22: Dead-Simple Async Control Flow with Coroutines

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/

Page 23: Dead-Simple Async Control Flow with Coroutines

“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.

Page 24: Dead-Simple Async Control Flow with Coroutines

Advanced Example: “Retry” with exponential backoff

Page 25: Dead-Simple Async Control Flow with Coroutines

Advanced Example: DB Cursoring

Page 26: Dead-Simple Async Control Flow with Coroutines

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!

Page 27: Dead-Simple Async Control Flow with Coroutines

THANK YOU SO MUCH! :D