├── .gitignore
├── README.md
├── main.js
├── package.json
├── src
├── index.html
└── scripts
│ ├── complexActions.js
│ ├── components
│ ├── createWord.js
│ └── main.js
│ ├── index.js
│ └── updates.js
└── www
├── app.js
└── index.html
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # csp-architecture
2 |
--------------------------------------------------------------------------------
/main.js:
--------------------------------------------------------------------------------
1 | import {chan, go, take, offer, put, putAsync, buffers, timeout} from 'js-csp';
2 |
3 | let ch = chan();
4 |
5 | let gen = id => function* () {
6 | console.log('Init id: ', id);
7 | while(true) {
8 | const v = yield take(ch);
9 | console.log(id, '===>', v);
10 | yield timeout(100);
11 | }
12 | }
13 |
14 | go(gen(1));
15 | //go(gen(2));
16 | //go(gen(3));
17 | //go(gen(4));
18 | //go(gen(5));
19 | //go(gen(6));
20 | //go(gen(7));
21 | // go(gen(8));
22 | // go(gen(9));
23 | // go(gen(10));
24 |
25 | go(function* () {
26 | let counter = 0;
27 | console.time('processes');
28 | while(counter < 100) {
29 | yield put(ch, counter);
30 | counter++;
31 | yield timeout(10);
32 | }
33 | console.timeEnd('processes');
34 | });
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "csp-js",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "main.js",
6 | "dependencies": {
7 | "js-csp": "^0.5.0",
8 | "react": "^0.13.3"
9 | },
10 | "devDependencies": {
11 | "babel": "^5.8.23",
12 | "babelify": "^6.3.0",
13 | "browserify": "^11.2.0",
14 | "http-server": "^0.8.4",
15 | "onchange": "^2.0.0",
16 | "watchify": "^3.4.0"
17 | },
18 | "scripts": {
19 | "test": "echo \"Error: no test specified\" && exit 1",
20 | "build": "npm run js && npm run html",
21 | "build:watch": "npm run js:watch && npm run html:watch",
22 | "html": "cp src/index.html www/",
23 | "html:watch": "onchange 'src/*.html' -v -- npm run html",
24 | "js": "browserify --debug src/scripts/index.js -t babelify --outfile www/app.js",
25 | "js:watch": "watchify --debug src/scripts/index.js -t babelify --outfile www/app.js -v",
26 | "start": "http-server ./www -p 3010",
27 | "deploy": "git subtree push --prefix www origin gh-pages"
28 | },
29 | "author": "",
30 | "license": "ISC"
31 | }
32 |
--------------------------------------------------------------------------------
/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Use CSP!
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/src/scripts/complexActions.js:
--------------------------------------------------------------------------------
1 | import {go, put, timeout} from 'js-csp';
2 |
3 | export const dbInsert = (updateChannels, newWord) => {
4 | go(function* () {
5 | yield put(updateChannels.loading, true);
6 |
7 | // do something costly
8 | yield timeout(1000);
9 | yield put(updateChannels.add, newWord);
10 |
11 | yield put(updateChannels.loading, false);
12 | });
13 | };
--------------------------------------------------------------------------------
/src/scripts/components/createWord.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {putAsync} from 'js-csp';
3 |
4 | export default class CreateWord extends React.Component {
5 | constructor(props) {
6 | super(props);
7 | this.state = {inputText: ''};
8 | }
9 |
10 | handleChange(event) {
11 | this.setState({inputText: event.target.value});
12 | }
13 |
14 | create() {
15 | const text = this.state.inputText.trim();
16 | const ch = this.props.complexActionsChannels.dbInsert;
17 | putAsync(ch, text);
18 | this.setState({inputText: ''});
19 | }
20 |
21 | render() {
22 | const loading = this.props.loading;
23 | return loading ? Adding word...
:
24 |
25 |
26 | Add...
27 |
28 | }
29 | }
--------------------------------------------------------------------------------
/src/scripts/components/main.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {putAsync} from 'js-csp';
3 |
4 | import CreateWord from './createWord';
5 |
6 | export default class Main extends React.Component {
7 | view(direction) {
8 | const ch = this.props.updateChannels.view;
9 | return () => putAsync(ch, direction);
10 | }
11 |
12 | render() {
13 | const state = this.props.appState;
14 | const currentWord = state.words[state.current];
15 | return
16 |
Current word: { currentWord }
17 |
18 | Previous
19 | Next
20 |
21 |
22 | { JSON.stringify(state, null, ' ') }
23 |
24 | }
25 | }
--------------------------------------------------------------------------------
/src/scripts/index.js:
--------------------------------------------------------------------------------
1 | import Polyfill from 'babel/polyfill';
2 | import {chan, go, take, put, putAsync, buffers} from 'js-csp';
3 |
4 | import React from 'react';
5 | import Main from './components/main';
6 |
7 | import * as Updates from './updates';
8 | import * as ComplexActions from './complexActions';
9 |
10 | const loadApp = () => ({
11 | state: {
12 | words: ['first', 'second', 'last'],
13 | current: 0,
14 | loading: false
15 | },
16 | updates: {
17 | channels: {
18 | view: chan(),
19 | add: chan(),
20 | loading: chan()
21 | },
22 | consumers: {
23 | view: Updates.view,
24 | add: Updates.add,
25 | loading: Updates.loading
26 | }
27 | },
28 | complexActions: {
29 | channels: {
30 | dbInsert: chan()
31 | },
32 | consumers: {
33 | dbInsert: ComplexActions.dbInsert
34 | }
35 | },
36 | renderCh: chan(buffers.sliding(1))
37 | });
38 |
39 | const initUpdates = app => {
40 | Object.keys(app.updates.consumers).forEach(k => {
41 | const updateFn = app.updates.consumers[k];
42 | const ch = app.updates.channels[k];
43 | go(function* () {
44 | while (true) {
45 | const value = yield take(ch);
46 | console.log(`On update channel [ ${k} ] received value [ ${JSON.stringify(value)} ]`);
47 | app.state = updateFn(app.state, value);
48 | yield put(app.renderCh, app.state);
49 | }
50 | });
51 | });
52 | };
53 |
54 | const initComplexActions = app => {
55 | Object.keys(app.complexActions.consumers).forEach(k => {
56 | const complexActionFn = app.complexActions.consumers[k];
57 | const ch = app.complexActions.channels[k];
58 | go(function* () {
59 | while (true) {
60 | const value = yield take(ch);
61 | console.log(`On complex action channel [ ${k} ] received value [ ${JSON.stringify(value)} ]`);
62 | complexActionFn(app.updates.channels, value);
63 | }
64 | });
65 | });
66 | };
67 |
68 | const initRender = (app, element) => {
69 | putAsync(app.renderCh, app.state);
70 |
71 | go(function* () {
72 | while(true) {
73 | const state = yield take(app.renderCh);
74 | const finishRender = chan();
75 | React.render(
76 | ,
80 | element,
81 | () => window.requestAnimationFrame(() => putAsync(finishRender, {})));
82 | yield take(finishRender);
83 | }
84 | });
85 | };
86 |
87 | const start = () => {
88 | let app = loadApp();
89 | window.app = app; // for debugging and testing
90 | initUpdates(app);
91 | initComplexActions(app);
92 | initRender(app, document.getElementById('main'));
93 | };
94 |
95 | start();
96 |
97 | window.csp = require('js-csp');
--------------------------------------------------------------------------------
/src/scripts/updates.js:
--------------------------------------------------------------------------------
1 | //
2 | // util functions
3 | //
4 | const clone = obj => JSON.parse(JSON.stringify(obj)); // naive but cool!
5 |
6 | const assoc = (obj, prop, value) => {
7 | const cl = clone(obj);
8 | cl[prop] = value;
9 | return cl;
10 | };
11 |
12 | const append = (array, value) => {
13 | const cl = clone(array);
14 | cl.push(value);
15 | return cl;
16 | };
17 |
18 | //
19 | // update functions
20 | //
21 | export const view = (state, direction) => {
22 | const nextCurrent = direction === 'next' ?
23 | Math.min(state.current + 1, state.words.length - 1) :
24 | Math.max(state.current - 1, 0);
25 |
26 | return assoc(state, 'current', nextCurrent);
27 | };
28 |
29 | export const add = (state, newWord) =>
30 | assoc(state, 'words', append(state.words, newWord));
31 |
32 | export const loading = (state, loadingState) =>
33 | assoc(state, 'loading', loadingState);
--------------------------------------------------------------------------------
/www/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Use CSP!
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------