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.

Communicating Sequential Processes (CSP) in JavaScript

1,707 views

Published on

Detailed overview of CSP in JavaScript using js-csp and some examples regarding production usage

Published in: Software
  • Be the first to comment

Communicating Sequential Processes (CSP) in JavaScript

  1. 1. Communicating SequentialProcesses (CSP)inJavaScript MaxKlymyshyn CTOatCartFresh
  2. 2. CartFresh ‣ Grocery Delivery startup ‣ WorkinginUkraineas ZAKAZ.UA (Kiev, Dnepropetrovsk,Kharkiv) andas CartFresh(Boston,US) ‣ React.js onfront-end ‣ Python onback-end
  3. 3. ‣ 12+yearsof experience: 7withPython,6 withJS ‣ Waspartof oDesk(Upwork),Helios,42cc ‣ Co-organizerof PyConUkraine,KyivJS ‣ CTOat CartFresh
  4. 4. AsynchronousI/O asynchronous I/OleadstocallbackAPI’s, which leadtonestedlambdas, whichleadto…thepyramidof doom:
  5. 5. range.on("preheat", function() { pot.on("boil", function() { rice.on("cooked", function() { dinner.serve(rice); }); }); }); Pyramidof doom
  6. 6. clusterfuck .
  7. 7. Understanding thecode
  8. 8. ‣ IBM1989: 50%of the effort inaccomplishing atask forthe programmer istowards understanding the system ‣ BellLabs1992:60%-80%of their time understanding code,20% as the developersgainexperience with current codebase ‣ NationalResearchCouncil1997(Canada): over25% of theirtimeeithersearchingfororlooking atcode ‣ Microsoft2006: 50% ‣ Peter Hallam2006:70%during personal experiment ‣ Microsoft2007:65%(survey)
  9. 9. CSP Communicatingsequentialprocesses
  10. 10. CSP ‣ initiallyisaformallanguage for describing patterns of interactionin concurrentsystems ‣ firstdescribedina1978paperby Tony Hoare ‣ influentialin thedesignof the occam, Go,Limbo ‣ core.async inclojure ‣ js-cspforJS
  11. 11. Keypoints ‣ CSPcreatedforcommunicating between differentcomponents andsubsystems ‣ CSPsolveproblemof coordinating anything asynchronous ‣ CSPalongsideotherssolveproblemof easy- to-understandcode
  12. 12. Channels
  13. 13. CHANNEL put take CoreAPI take timeout
  14. 14. ‣ blockingsemantic ‣ duplex ‣ indirect processcommunication ‣ easy-to-implementremotechannels ChannelProperties
  15. 15. example
  16. 16. 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(); });
  17. 17. Features
  18. 18. merge Read frommultiple channels CHANNEL1 CHANNEL2 put put
  19. 19. Read frommultiple 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; }; });
  20. 20. put CHANNEL1 CHANNEL2take take Supplyvalueintomultiplechannels
  21. 21. Supplyvalueintomultiplechannels 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; } });
  22. 22. put Reducechannel values CHANNEL1 CHANNEL2 REDUCE take take
  23. 23. Reducechannel 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();
  24. 24. put Buffering CHANNEL 1 BUFFER take
  25. 25. Features ‣ Channel buffering:fixedsize,sliding, dropping ‣ poll/ offer values:taking/putting immediately ‣ alts:getvalueorexecutesecondoperation Commonprocesses communication features
  26. 26. ‣ Mixing channels withmute/unmute ‣ Pub/Sub mode ‣ Filteringwithpredicatesand/ortransducers
  27. 27. CHANNEL PROCESS 2 PROCESS 1 put(1) take Communicationexample FLOW take put(2)
  28. 28. 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
  29. 29. 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
  30. 30. 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?
  31. 31. JavaScript,baby
  32. 32. 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
  33. 33. 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
  34. 34. CHANNEL PROCESS 2 PROCESS 1 put take wait put take wait take put Channel FLOW
  35. 35. CHANNEL PROCESS 2 PROCESS 1 put put FLOW PROCESS 3 take take take put take put
  36. 36. real-world
  37. 37. 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>); }}
  38. 38. 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")); } }); }
  39. 39. synccode
  40. 40. Toolstowritein syncstyle? ‣ Promises,Promises withgenerators ‣ Generators ‣ Async/await ‣ Using actors,RxJS,js-csp
  41. 41. synccode withjs-csp
  42. 42. Tools ‣ Events ‣ XMLHttpRequest/HTTP ‣ WebSockets ‣ Timers ‣ Webworkers Runtimefor low-levelasync operations: getURL
  43. 43. 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; }
  44. 44. 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.'; });
  45. 45. 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; }
  46. 46. isoroutes Pure,isomorphic andframework-agnostic js-csp—based router
  47. 47. Purerouter ‣ Shouldbepure ‣ Statebasedonlyon input ‣ Framework-agnostic ‣ No promises,sync-stylecode
  48. 48. 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)], ]);
  49. 49. Gatherstate export 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() { // ... } }
  50. 50. @maxmaxmaxmax Questions?

×