Last active
August 29, 2015 14:10
-
-
Save zerokarmaleft/c87c5530aaa1a8cd6a4b to your computer and use it in GitHub Desktop.
core.async and js-csp side-by-side
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(ns vending-machine.core | |
(:require [cljs.core.async | |
:refer [<! >! chan timeout]] | |
[figwheel.client :as figwheel | |
:include-macros [true]] | |
[om.core :as om | |
:include-macros true] | |
[sablono.core :as html | |
:refer-macros [html]]) | |
(:require-macros [cljs.core.async.macros | |
:refer [go go-loop alt!]])) | |
(enable-console-print!) | |
;; (figwheel/watch-and-reload :websocket-url "ws://localhost:3449/figwheel-ws") | |
(defn customer [name coins chocolates toffees] | |
(go | |
(<! (timeout (* (Math/random) 60000))) | |
(println (str name ": inserting coin...")) | |
(>! coins 1) | |
(println (str name ": coin inserted!")) | |
(let [candies (if (> (Math/random) 0.5) chocolates toffees)] | |
(println (str name ": got a " (<! candies) "."))))) | |
(defn vending-machine [{:keys [name coins chocolates toffees]} owner] | |
(reify | |
om/IInitState | |
(init-state [_] | |
{:coins-inserted 0 | |
:chocolates-taken 0 | |
:toffees-taken 0}) | |
om/IWillMount | |
(will-mount [this] | |
(go-loop [] | |
(println "Waiting for customer to insert coin...") | |
(<! coins) | |
(println "Coin inserted!") | |
(om/update-state! owner :coins-inserted inc) | |
(println "Waiting for customer to take candy...") | |
(alt! | |
[[chocolates :chocolate]] (om/update-state! owner :chocolates-taken inc) | |
[[toffees :toffee]] (om/update-state! owner :toffees-taken inc)) | |
(println "Candy taken!") | |
(recur))) | |
om/IRenderState | |
(render-state [_ {:keys [coins-inserted chocolates-taken toffees-taken]}] | |
(html | |
[:div | |
[:h2 (str "Vending Machine (" name ")")] | |
[:div {:id "status"} | |
[:h4 "Status"] | |
[:ul | |
[:li (str "Coins inserted: " coins-inserted)] | |
[:li (str "Chocolates taken: " chocolates-taken)] | |
[:li (str "Toffees taken: " toffees-taken)]]]])))) | |
(defn make-channels [n] | |
(take n (repeatedly chan))) | |
(defn vending-machine-grid [props owner] | |
(reify | |
om/IWillMount | |
(will-mount [this] | |
(let [coins (make-channels (:size props)) | |
chocolates (make-channels (:size props)) | |
toffees (make-channels (:size props)) | |
names (map #(str "[Customer " % "]") | |
(range 1 (inc (* (:size props) (:num-customers props))))) | |
customers (->> (interleave names (cycle coins) (cycle chocolates) (cycle toffees)) | |
(partition 4))] | |
(om/set-state! owner {:coins coins | |
:chocolates chocolates | |
:toffees toffees}) | |
(doseq [[name coins chocolates toffees] customers] | |
(go | |
(println (str name ": getting in line.")) | |
(customer name coins chocolates toffees))))) | |
om/IRenderState | |
(render-state [_ {:keys [coins chocolates toffees]}] | |
(html | |
[:table | |
[:thead] | |
[:tbody | |
(for [n (range (:size props))] | |
[:tr {:key (str "vm" n)} | |
[:td | |
(om/build vending-machine {:name (str "vm" n) | |
:coins (nth coins n) | |
:chocolates (nth chocolates n) | |
:toffees (nth toffees n)})]])]])))) | |
(om/root vending-machine-grid | |
{:size 4, :num-customers 2500} | |
{:target (.getElementById js/document "vending-machine")}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* @jsx React.DOM | |
*/ | |
var csp = require('csp'); | |
var VendingMachine = React.createClass({ | |
getInitialState: function() { | |
return { | |
coinsInserted: 0, | |
chocolatesDispensed: 0, | |
toffeesDispensed: 0 | |
}; | |
}, | |
componentDidMount: function() { | |
csp.go(this.serveCustomers, [this.props.coins, this.props.chocolates, this.props.toffees]); | |
}, | |
render: function() { | |
return ( | |
<div> | |
<h2>Vending Machine ({this.props.name})</h2> | |
<div id='status'> | |
<h4>Status</h4> | |
<ul> | |
<li>Coins inserted: {this.state.coinsInserted}</li> | |
<li>Chocolates dispensed: {this.state.chocolatesDispensed}</li> | |
<li>Toffees dispensed: {this.state.toffeesDispensed}</li> | |
</ul> | |
</div> | |
</div> | |
); | |
}, | |
serveCustomers: function*(coins, chocolates, toffees){ | |
while (true) { | |
console.log("Waiting for customer to insert coin..."); | |
yield csp.take(coins); | |
this.setState({ coinsInserted: this.state.coinsInserted + 1 }); | |
console.log("Coin inserted!"); | |
this.setState({ coinInserted: true }); | |
console.log("Waiting for customer to retrieve candy..."); | |
var candy = yield csp.alts([[chocolates, 'chocolate'], [toffees, 'toffee']]); | |
if (candy.channel === chocolates) { | |
this.setState({ chocolatesDispensed: this.state.chocolatesDispensed + 1 }); | |
} else if (candy.channel === toffees) { | |
this.setState({ toffeesDispensed: this.state.toffeesDispensed + 1 }); | |
} | |
console.log("Candy retrieved!"); | |
this.setState({ coinInserted: false }); | |
} | |
} | |
}); | |
var VendingMachineGrid = React.createClass({ | |
render: function() { | |
var gridSize = this.props.gridSize, | |
coins = this.props.coins, | |
chocolates = this.props.chocolates, | |
toffees = this.props.toffees; | |
var vms = _.chain(_.range(gridSize)) | |
.map(function(vm, n) { | |
return ( | |
<tr> | |
<td> | |
<VendingMachine name = {'vm' + n} | |
coins = {coins[n]} | |
chocolates = {chocolates[n]} | |
toffees = {toffees[n]} /> | |
</td> | |
</tr> | |
); | |
}); | |
return ( | |
<table> | |
<thead> | |
</thead> | |
<tbody> | |
{vms} | |
</tbody> | |
</table> | |
); | |
} | |
}); | |
var VendingMachineSim = React.createClass({ | |
componentDidMount: function() { | |
var coins = this.props.coins, | |
chocolates = this.props.chocolates, | |
toffees = this.props.toffees; | |
for (var i = 0; i < this.props.gridSize; i++) { | |
for (var j = 0; j < this.props.numCustomers; j++) { | |
var id = (i+1) * (j+1); | |
console.log('[Customer ' + id + ']: getting in line.'); | |
csp.go(this.customer, ['[Customer ' + id + ']', | |
coins[i], chocolates[i], toffees[i]]); | |
} | |
} | |
}, | |
render: function() { | |
return ( | |
<VendingMachineGrid gridSize = {this.props.gridSize} | |
coins = {this.props.coins} | |
chocolates = {this.props.chocolates} | |
toffees = {this.props.toffees} /> | |
); | |
}, | |
customer: function*(name, coins, chocolates, toffees) { | |
// wait for a random amount of time | |
yield csp.take(csp.timeout(Math.random() * 60000)); | |
// make a random candy selection | |
var candies = (Math.random() > 0.5) ? chocolates : toffees; | |
console.log(name + ': inserting coin...'); | |
yield csp.put(coins, 1); | |
console.log(name + ': coin inserted!'); | |
console.log(name + ': retrieving candy...'); | |
var candy = yield csp.take(candies); | |
console.log(name + ': got a ' + candy + '.'); | |
} | |
}); | |
var gridSize = 4; | |
var numCustomers = 2500; | |
function makeChannels(n) { | |
var channels = []; | |
for (var i = 0; i < n; i++) { | |
channels.push(csp.chan()); | |
} | |
return channels; | |
} | |
React.render( | |
<VendingMachineSim gridSize = {gridSize} | |
numCustomers = {numCustomers} | |
coins = {makeChannels(gridSize)} | |
chocolates = {makeChannels(gridSize)} | |
toffees = {makeChannels(gridSize)} | |
/>, | |
document.getElementById('vending-machine') | |
); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment