communicating sequential processes (csp) in javascript

57
Communicating Sequential Processes (CSP) in JavaScript Max Klymyshyn CTO at CartFresh

Upload: max-klymyshyn

Post on 13-Feb-2017

447 views

Category:

Software


0 download

TRANSCRIPT

Page 1: Communicating Sequential Processes (CSP) in JavaScript

Communicating Sequential Processes (CSP) in JavaScript

Max Klymyshyn CTO at CartFresh

Page 2: Communicating Sequential Processes (CSP) in JavaScript

CartFresh‣ 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: Communicating Sequential Processes (CSP) in JavaScript

‣ 12+ years of experience: 7 with Python, 6 with JS

‣ Was part of oDesk (Upwork), Helios, 42cc

‣ Co-organizer of PyCon Ukraine, KyivJS

‣ CTO at CartFresh

Page 4: Communicating Sequential Processes (CSP) in JavaScript

Asynchronous I/Oasynchronous I/O leads to callback API’s,

which lead to nested lambdas, which lead to… the pyramid of doom:

Page 5: Communicating Sequential Processes (CSP) in JavaScript

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

Pyramid of doom

Page 6: Communicating Sequential Processes (CSP) in JavaScript

clusterfuck.

Page 7: Communicating Sequential Processes (CSP) in JavaScript
Page 8: Communicating Sequential Processes (CSP) in JavaScript

Understanding the code

Page 9: Communicating Sequential Processes (CSP) in JavaScript

‣ 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)

Page 10: Communicating Sequential Processes (CSP) in JavaScript

CSP Communicating sequential processes

Page 11: Communicating Sequential Processes (CSP) in JavaScript

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 12: Communicating Sequential Processes (CSP) in JavaScript

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 13: Communicating Sequential Processes (CSP) in JavaScript

Channels

Page 14: Communicating Sequential Processes (CSP) in JavaScript

CHANNEL

put

take

Core API

take timeout

Page 15: Communicating Sequential Processes (CSP) in JavaScript

‣ blocking semantic

‣ duplex

‣ indirect process communication

‣ easy-to-implement remote channels

Channel Properties

Page 16: Communicating Sequential Processes (CSP) in JavaScript

example

Page 17: Communicating Sequential Processes (CSP) in JavaScript

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(2000)); yield put(ch, 2); ch.close(); });

Page 18: Communicating Sequential Processes (CSP) in JavaScript
Page 19: Communicating Sequential Processes (CSP) in JavaScript

Features

Page 20: Communicating Sequential Processes (CSP) in JavaScript

merge

Read from multiple channels

CH

AN

NEL

1

CH

AN

NEL

2

put put

Page 21: Communicating Sequential Processes (CSP) in JavaScript

Read from multiple channels

var chan1 = chan(), chan2 = chan(), merged = operations.merge([chan1, chan2]);

go(function*(){ var value = yield merged; while (value !== CLOSED) { console.log("Got ", value); value = yield merged; }; });

Page 22: Communicating Sequential Processes (CSP) in JavaScript

put

CH

AN

NEL

1

CH

AN

NEL

2take take

Supply value into multiple channels

Page 23: Communicating Sequential Processes (CSP) in JavaScript

Supply value into multiple channels

var src = chan(), mult = operations.mult(src), chan1 = chan(), chan2 = chan(0);

operations.mult.tap(mult, chan1); operations.mult.tap(mult, chan2);

go(function * () { yield put(src, 1); });

go(function * () { var value = yield chan1; while (value !== CLOSED) { console.log("Got ", value, " in `chan1`"); value = yield chan1; } });

Page 24: Communicating Sequential Processes (CSP) in JavaScript

put

Reduce channel values

CH

AN

NEL

1

CH

AN

NEL

2

RED

UC

Etake

take

Page 25: Communicating Sequential Processes (CSP) in JavaScript

Reduce channel values

var ch = chan(), append = (a, b) => a + " " + b;

var reduceCh = operations.reduce(append, "Hello", ch);

go(function * () { yield put(ch, "CSP"); yield put(ch, "World"); console.log(yield reduceCh); });

ch.close();

Page 26: Communicating Sequential Processes (CSP) in JavaScript

put

Buffering

CHANNEL 1

BUFFER

take

Page 27: Communicating Sequential Processes (CSP) in JavaScript

Features

‣ Channel buffering: fixed size, sliding, dropping

‣ poll / offer values: taking/putting immediately

‣ alts: get value or execute second operation

Common processes communication features

Page 28: Communicating Sequential Processes (CSP) in JavaScript

‣ Mixing channels with mute/unmute

‣ Pub/Sub mode

‣ Filtering with predicates and/or transducers

Page 29: Communicating Sequential Processes (CSP) in JavaScript

CHANNEL

PROCESS 2

PROCESS 1

put(1)

take

Communication example

FLOW

take

put(2)

Page 30: Communicating Sequential Processes (CSP) in JavaScript

import {go, chan, take, timeout, put} from "js-csp"; var ch = chan();

go(function*() { log(1, "line 1"); log(1, " line 2"); log(1, " <- take"); log(1, " took: process 2: ", yield take(ch)); log(1, " line 5"); log(1, " <- take"); log(1, " took: process 2: ", yield take(ch)); log(1, " line 8");

});

go(function*() { log(2, "line 1"); log(2, " -> put"); yield put(ch, 1); log(2, " line 4"); log(2, " -> put"); yield put(ch, 2); log(2, " line 7"); });

sync point

Page 31: Communicating Sequential Processes (CSP) in JavaScript

p( 1 ): line 1 p( 1 ): line 2 p( 1 ): <- take p( 2 ): line 1 p( 2 ): -> put p( 2 ): line 4 p( 2 ): -> put p( 1 ): took: process 2: val: 1 p( 1 ): line 5 p( 1 ): <- take p( 2 ): line 7 p( 1 ): took: process 2: val: 2 p( 1 ): line 8

Page 32: Communicating Sequential Processes (CSP) in JavaScript

p( 1 ): line 1 p( 1 ): line 2 p( 1 ): <- take p( 2 ): line 1 p( 2 ): -> put p( 2 ): line 4 p( 2 ): -> put p( 1 ): took: process 2: val: 1 p( 1 ): line 5 p( 1 ): <- take p( 2 ): line 7 p( 1 ): took: process 2: val: 2 p( 1 ): line 8

what the fuck?

Page 33: Communicating Sequential Processes (CSP) in JavaScript

JavaScript, baby

Page 34: Communicating Sequential Processes (CSP) in JavaScript

p( 1 ): line 1 p( 1 ): line 2 p( 1 ): <- take p( 2 ): line 1 p( 2 ): -> offer p( 1 ): took: process 2: val: 1 p( 1 ): line 5 p( 1 ): <- take p( 2 ): line 4 p( 2 ): -> offer p( 2 ): line 7 p( 1 ): took: process 2: val: 2 p( 1 ): line 8

Page 35: Communicating Sequential Processes (CSP) in JavaScript

import {go, chan, put, buffers, offer} from "js-csp";

var ch = chan();

go(function*() { log(1, "line 1"); log(1, " line 2"); log(1, " <- take"); log(1, " took: process 2: ", yield take(ch)); log(1, " line 5"); log(1, " <- take"); log(1, " took: process 2: ", yield take(ch)); log(1, " line 8");

});

go(function*() { log(2, "line 1"); log(2, " -> offer"); yield offer(ch, 1); yield timeout(0); log(2, " line 4"); log(2, " -> offer"); yield offer(ch, 2); log(2, " line 7"); });

sync point

Page 36: Communicating Sequential Processes (CSP) in JavaScript

CHANNEL

PROCESS 2

PROCESS 1

put

take

wait

put

take

wait

take

put

Channel

FLOW

Page 37: Communicating Sequential Processes (CSP) in JavaScript

CHANNEL

PROCESS 2

PROCESS 1

put

put

FLOW

PROCESS 3

take

take

take

put

take

put

Page 38: Communicating Sequential Processes (CSP) in JavaScript
Page 39: Communicating Sequential Processes (CSP) in JavaScript

real-world

Page 40: Communicating Sequential Processes (CSP) in JavaScript

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 41: Communicating Sequential Processes (CSP) in JavaScript

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 42: Communicating Sequential Processes (CSP) in JavaScript
Page 43: Communicating Sequential Processes (CSP) in JavaScript
Page 44: Communicating Sequential Processes (CSP) in JavaScript

sync code

Page 45: Communicating Sequential Processes (CSP) in JavaScript

Tools to write in sync style?

‣ Promises, Promises with generators

‣ Generators

‣ Async/await

‣ Using actors, RxJS, js-csp

Page 46: Communicating Sequential Processes (CSP) in JavaScript

sync code with js-csp

Page 47: Communicating Sequential Processes (CSP) in JavaScript

Tools

‣ Events

‣ XMLHttpRequest/HTTP

‣ WebSockets

‣ Timers

‣ Web workers

Runtime for low-level async operations: get URL

Page 48: Communicating Sequential Processes (CSP) in JavaScript

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

export function listen(el, type, options={}) { /** * Translate events into CSP channel until 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 49: Communicating Sequential Processes (CSP) in JavaScript

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 50: Communicating Sequential Processes (CSP) in JavaScript
Page 51: Communicating Sequential Processes (CSP) in JavaScript

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 52: Communicating Sequential Processes (CSP) in JavaScript

isoroutesPure, isomorphic and framework-agnostic

js-csp—based router

Page 53: Communicating Sequential Processes (CSP) in JavaScript

Pure router

‣ Should be pure

‣ State based only on input

‣ Framework-agnostic

‣ No promises, sync-style code

Page 54: Communicating Sequential Processes (CSP) in JavaScript

Example

import {render, router, navigate} from "isoroutes";

exports var routes = router([ ["/", render.react(Home)], ["/about/", render.react(About)], ["/contacts/", render.react(Contact)], ["/articles/:id.html", render.react(Article)], ["/dashboard/", render.react(Dashboard)], ["/dashboard/signin/", render.react(Auth)], ["/dashboard/add/", render.react(ArticleForm)], ["/dashboard/:menu/", render.react(Dashboard)], ]);

Page 55: Communicating Sequential Processes (CSP) in JavaScript

Gather stateexport class Home extends React.Component { // state will be executed within CSP `go` routine static state(state, channel, n=0) { // we could use CSP channels here return go(function * () { yield put(channel, [ "talks", yield take(json({url: "/api/upcoming_talks.json"})) ]); channel.close() }) } render() { // ... } }

Page 56: Communicating Sequential Processes (CSP) in JavaScript
Page 57: Communicating Sequential Processes (CSP) in JavaScript

@maxmaxmaxmaxQuestions?