Fighting async JavaScript (CSP)

37
Max Klymyshyn CartFresh Fighting async JavaScript

Transcript of Fighting async JavaScript (CSP)

Page 1: Fighting async JavaScript (CSP)

Max KlymyshynCartFresh

Fighting async JavaScript

Page 2: Fighting async JavaScript (CSP)

CartFresh GVMachines Inc.

‣ Grocery Delivery startup

‣ Working in Ukraine as ZAKAZ.UA (Kiev, Dnepropetrovsk, Kharkiv) and as CartFresh (Boston, US)

‣ React.js on front-end

‣ Python on back-end

Page 3: Fighting async JavaScript (CSP)

the problem

Page 4: Fighting async JavaScript (CSP)

Asynchronous I/O

asynchronous I/O leads to callback API’s, which lead to nested lambdas,

which lead to… the pyramid of doom:

Page 5: Fighting async JavaScript (CSP)

range.on("preheat", function() { pot.on("boil", function() { rice.on("cooked", function() { dinner.serve(rice); }); }); });

Pyramid of doom

Page 6: Fighting async JavaScript (CSP)

clusterfuck.

Page 7: Fighting async JavaScript (CSP)

Understanding the code

Page 8: Fighting async JavaScript (CSP)

‣ IBM 1989: 50% of the effort in accomplishing a task for the programmer is towards understanding the system

‣ Bell Labs 1992: 60%-80% of their time understanding code, 20% as the developers gain experience with current codebase

‣ National Research Council 1997 (Canada): over 25% of their time either searching for or looking at code

‣ Microsoft 2006: 50%

‣ Peter Hallam 2006: 70% during personal experiment

‣ Microsoft 2007: 65% (survey)[1]

Page 9: Fighting async JavaScript (CSP)
Page 10: Fighting async JavaScript (CSP)

Promisesvar greetingPromise = sayHello();

greetingPromise .then(addExclamation) .then(function (greeting) { console.log(greeting); // hello world!!!! }, function(error) { // 'uh oh: something bad happened console.error('uh oh: ', error); });

Page 11: Fighting async JavaScript (CSP)

function loadStory() { return getJSON('story.json').then(function(story) { addHtmlToPage(story.heading);

return story.chapterURLs.map(getJSON) .reduce(function(chain, chapterPromise) { return chain.then(function() { return chapterPromise; }).then(function(chapter) { addHtmlToPage(chapter.html); }); }, Promise.resolve()); }).then(function() { addTextToPage("All done"); }).catch(function(err) { addTextToPage("Argh, broken: " + err.message); }).then(function() { document.querySelector('.spinner').style.display = 'none'; }); }

Page 12: Fighting async JavaScript (CSP)

Awaitasync function loadStory() { try { let story = await getJSON('story.json'); addHtmlToPage(story.heading); for (let chapter of story.chapterURLs.map(getJSON)) { addHtmlToPage((await chapter).html); } addTextToPage("All done"); } catch (err) { addTextToPage("Argh, broken: " + err.message); } document.querySelector('.spinner').style.display = 'none'; }

(async function() { await loadStory(); console.log("Yey, story successfully loaded!"); }());

[5] [6] [7]

Page 13: Fighting async JavaScript (CSP)

FRP/RxJSconst $input = $('#input'); const $results = $('#results');

/* Only get the value from each key up */ var keyups = Rx.Observable.fromEvent($input, 'keyup') .pluck('target', 'value') .filter(text => text.length > 2 );

/* Now debounce the input for 500ms */ var debounced = keyups.debounce(500 /* ms */);

/* Now get only distinct values, so we eliminate the arrows and other control characters */ var distinct = debounced.distinctUntilChanged();

[4]

Page 14: Fighting async JavaScript (CSP)

Actor model(function () { var spawn = WebActors.spawn; var receive = WebActors.receive; var send = WebActors.send, ANY = WebActors.ANY;

function aCallback() { receive(ANY, function (message) { alert(message); }); }

actor = spawn(aCallback); // create an actor send(actor, "a message"); // send it a message

})();

[2] [3]

Page 15: Fighting async JavaScript (CSP)

CSP Communicating sequential processes

Page 16: Fighting async JavaScript (CSP)

CSP

‣ initially is a formal language for describing patterns of interaction in concurrent systems

‣ first described in a 1978 paper by Tony Hoare

‣ influential in the design of the occam, Go, Limbo

‣ core.async in clojure

‣ js-csp for JS

Page 17: Fighting async JavaScript (CSP)

Key points

‣ CSP created for communicating between different components and subsystems

‣ CSP solve problem of coordinating anything asynchronous

‣ CSP alongside others solve problem of easy-to-understand code

Page 18: Fighting async JavaScript (CSP)

example

Page 19: Fighting async JavaScript (CSP)

import {chan, take, CLOSED, timeout, put} from "js-csp";

var ch = chan();

go(function*() { var val; while((val = yield take(ch)) !== CLOSED) { console.log(val); } });

go(function*() { yield put(ch, 1); yield take(timeout(1000)); yield put(ch, 2); ch.close(); });

[8]

Page 20: Fighting async JavaScript (CSP)
Page 21: Fighting async JavaScript (CSP)

real-world

Page 22: Fighting async JavaScript (CSP)

export class Suggest extends React.Component { constructor(props) { super(props); this.state = {active: -1, suggests: props.suggests}} componentWillReceiveProps(nextProps) { switch(nextProps.code) { case 38: this.setState({active: Math.max(-1, this.state.active - 1)}); break; case 40: this.setState({ active: Math.min(this.props.suggests.length - 1, this.state.active + 1)}); break; case 13: search(this.props.suggests[this.state.active]); this.setState({suggests: []}); break; default: this.setState({suggests: nextProps.suggests}); } } render() { return (<ul className="dropdown dropdown-menu"> {this.state.suggests.map((e, n) => <li key={e} className={...}><a href="#">{e}</a></li>)} </ul>); }}

Page 23: Fighting async JavaScript (CSP)

function listen(el, type) { var ch = chan(); el.addEventListener(type, e => { putAsync(ch, [e.keyCode, el.value]); e.preventDefault(); }); return ch; }

export function suggest(elem) { var el = elem[0];

go(function * () { var keydown = listen(el, 'keydown');

while(true) { var [code, value] = yield take(keydown); var response = yield take(Storage.sync([ ["store.suggest", {query: value}, {id: "suggest"}]]));

ReactDOM.render( <Suggest suggests={response.suggests || []} code={code} value={value} />, document.getElementById("suggest")); } }); }

Page 24: Fighting async JavaScript (CSP)
Page 25: Fighting async JavaScript (CSP)
Page 26: Fighting async JavaScript (CSP)

sync code

Page 27: Fighting async JavaScript (CSP)

Tools to write in sync style?

‣ Promises, Promises with generators

‣ Generators

‣ Async/wait

‣ Using js-csp (with generators)

Page 28: Fighting async JavaScript (CSP)

sync code with js-csp

Page 29: Fighting async JavaScript (CSP)

Tools

‣ Events

‣ XMLHttpRequest/HTTP

‣ WebSockets

‣ Timers

‣ Web workers

Runtime for low-level async operations: get URL

Page 30: Fighting async JavaScript (CSP)

import {buffers, go, chan, putAsync, operations} from "js-csp";

export function listen(el, type, options={}) { /** * Translate events into CSP channel untile channel is not closed. */ var {channel, prevent} = options;

var ch = channel || chan();

var listener = (e) => { if (ch.closed === true) el.removeEventListener(type, listener); else putAsync(ch, e); if (prevent === true) e.preventDefault(); }

el.addEventListener(type, listener);

return ch; }

Page 31: Fighting async JavaScript (CSP)

import {go, take, timeout, CLOSED, close, chan, buffers} from "js-csp"; import {listen} from "./runtime.js";

var mousemove = listen(document, "mousemove", true, {channel: chan(buffers. dropping(1))}); var target = document.getElementById("coords");

go(function * () { var coords; while((coords = yield take(mousemove)) !== CLOSED) { target.innerHTML = `X=${coords.clientX} Y=${coords.clientY}`; } });

go(function * () { yield timeout(3000); yield mousemove.close(); target.innerHTML = 'interrupted.'; });

Page 32: Fighting async JavaScript (CSP)
Page 33: Fighting async JavaScript (CSP)

import {buffers, go, chan, putAsync, take,} from "js-csp";

export function json(options) { var ch = chan(); go(function * () { var value = yield take(request(options)); if(!(value instanceof Error)) { value = JSON.parse(value); } else { console.error("Can't get " + options.url, value); } putAsync(ch, value); });

return ch; }

Page 34: Fighting async JavaScript (CSP)

Features

‣ Channel buffering: fixed size, sliding, dropping

‣ poll values: taking immediately

‣ alts: wait for value or execute second operation

Common processes communication features

Page 35: Fighting async JavaScript (CSP)

Extra features‣ Reduce channel values

‣ split values of channels

‣ merge channels

‣ mult – supply values into two channels

‣ Pub/Sub mode

‣ Filtering with predicates and/or transducers

Page 36: Fighting async JavaScript (CSP)

thanks.

@maxmaxmaxmax

Page 37: Fighting async JavaScript (CSP)

References

1. No, You are not Dumb! Programmers do spend a lot of time Understanding Code… http://blog.architexa.com/2010/05/no-you-are-not-dumb-programmers-do-spend-a-lot-of-time-understanding-code/

2. An Actor model example with Akka.NET http://blog.geist.no/an-actor-model-example-with-akka-net/

3. WebActors https://github.com/mental/webactors

4. The Reactive Extensions for JavaScript (RxJS) https://github.com/Reactive-Extensions/RxJS

5. ES7 async functions - a step in the wrong direction https://spion.github.io/posts/es7-async-await-step-in-the-wrong-direction.html

6. Why coroutines won’t work on the web http://calculist.org/blog/2011/12/14/why-coroutines-wont-work-on-the-web/

7. Implementation Strategies for First-Class Continuations* http://citeseerx.ist.psu.edu/viewdoc/download;jsessionid=3668F1FC41AF1C1880B3062100057381?doi=10.1.1.70.9076&rep=rep1&type=pdf

8. Taming the Asynchronous Beast with CSP Channels in JavaScript http://jlongster.com/Taming-the-Asynchronous-Beast-with-CSP-in-JavaScript