Pauan

SJS Channel vs Emitter 01

Sep 23rd, 2014
441
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. @ = require(['mho:std', 'mho:app']);
  2.  
  3.  
  4. // channel.sjs
  5. ;(function (exports) {
  6.   exports.pending_limit = 1024
  7.  
  8.   exports.closed = {}
  9.  
  10.  
  11.   function wake(array, value) {
  12.     var f = array.shift()
  13.     return f(value)
  14.   }
  15.  
  16.   function sleep(chan_this, value) {
  17.     if (chan_this.length < exports.pending_limit) {
  18.       // TODO why doesn't this hold work ?
  19.       // hold(0)
  20.       waitfor (var result) {
  21.         function done(x) {
  22.           // TODO is this a good spot for this ?
  23.           // TODO is there a better way than using hold(0) ?
  24.           hold(0)
  25.           resume(x)
  26.           return value
  27.         }
  28.         chan_this.push(done)
  29.       } retract {
  30.         var index = chan_this.indexOf(done)
  31.         console.log("RETRACTED", index)
  32.         @assert.isNot(index, -1) // TODO test if this is ever possible
  33.         if (index !== -1) {
  34.           chan_this.splice(index, 1)
  35.         }
  36.       }
  37.       return result
  38.     } else {
  39.       throw new Error("cannot have more than #{exports.pending_limit} pending operations on a channel")
  40.     }
  41.   }
  42.  
  43.  
  44.   // The behavior is the same as Clojure's core.async channels (http://clojure.github.io/core.async/)
  45.   exports.Channel = function () {
  46.     // TODO what about pending receiver/sender when retracted ?
  47.     var o = @Stream(function (emit) {
  48.       while (true) {
  49.         // TODO I would like to use `exports.take(this)`, but I can't
  50.         var v = exports.take(o)
  51.         if (v === exports.closed) {
  52.           break
  53.         } else {
  54.           emit(v)
  55.         }
  56.       }
  57.     })
  58.  
  59.     o.closed   = false
  60.     o.receiver = []
  61.     o.sender   = []
  62.  
  63.     return o
  64.   }
  65.  
  66.   exports.take = function (channel) {
  67.     if (channel.sender.length) {
  68.       return wake(channel.sender, null)
  69.     } else if (channel.closed) {
  70.       return exports.closed
  71.     } else {
  72.       return sleep(channel.receiver, null)
  73.     }
  74.   }
  75.  
  76.   // TODO this used to return something even though it didn't use return! find out why
  77.   exports.put = function (channel, value) {
  78.     if (channel.closed) {
  79.       return false
  80.     } else if (channel.receiver.length) {
  81.       wake(channel.receiver, value)
  82.       return true
  83.     } else {
  84.       sleep(channel.sender, value)
  85.       return true
  86.     }
  87.   }
  88.  
  89.   exports.close = function (channel) {
  90.     channel.closed = true
  91.  
  92.     while (channel.receiver.length) {
  93.       wake(channel.receiver, exports.closed)
  94.     }
  95.   }
  96.  
  97.   exports.isClosed = function (channel) {
  98.     return channel.closed
  99.   }
  100. })(@)
  101.  
  102.  
  103. // event.sjs
  104. ;(function (exports) {
  105.   exports.Emitter = function () {
  106.     var o = @Stream(function (emit) {
  107.       // TODO I would like to use `this` rather than `o`
  108.       var c = exports.on(o)
  109.       try {
  110.         c ..@each(emit)
  111.       } finally {
  112.         exports.off(o, c)
  113.       }
  114.     })
  115.  
  116.     o.listeners = []
  117.  
  118.     return o
  119.   }
  120.  
  121.   exports.on = function (emitter) {
  122.     var c = @Channel()
  123.     emitter.listeners.push(c)
  124.     return c
  125.   }
  126.  
  127.   exports.off = function (emitter, c) {
  128.     emitter.listeners ..@remove(c)
  129.   }
  130.  
  131.   exports.emit = function (emitter, value) {
  132.     // TODO use each.par instead ?
  133.     emitter.listeners ..@each(function (listener) {
  134.       listener ..@put(value)
  135.     })
  136.   }
  137. })(@)
  138.  
  139.  
  140. // dom.sjs
  141. ;(function (exports) {
  142.   exports.events = function (elem, name, info) {
  143.     var o = @Emitter()
  144.     elem.addEventListener(name, function (e) {
  145.       if (info != null) {
  146.         if (info.preventDefault) {
  147.           e.preventDefault()
  148.         }
  149.       }
  150.       o ..@emit(e)
  151.     }, true)
  152.     return o
  153.   }
  154. })(@)
  155.  
  156.  
  157. function getUpdateFromServer() {
  158.   hold(1000); // simulate server round trip
  159.   return @Div('This is an update from the server');
  160. }
  161.  
  162. @mainContent .. @appendContent(@Btn('default', 'Get status update from server')) {
  163.   |button|
  164.  
  165.   button .. @events('click') ..@buffer(1, { drop: true }) .. @each {
  166.     ||
  167.     waitfor {
  168.       @mainContent .. @appendContent(getUpdateFromServer());
  169.     }
  170.     or {
  171.       hold(100); // wait a little while before showing feedback
  172.  
  173.       // actually want to append to status bar here or something, but this is for demo only
  174.       @mainContent .. @appendContent('waiting for server...') { || hold() }
  175.     }
  176.   }
  177. }
Advertisement
Add Comment
Please, Sign In to add comment