Created
March 27, 2011 15:01
-
-
Save nowelium/889276 to your computer and use it in GitHub Desktop.
javascript Actor: scala's actor style
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
Function.prototype.bind = function(obj){ | |
var __method__ = this; | |
var __args__ = Array.prototype.slice.call(arguments); | |
__args__.shift(); // obj | |
return function (){ | |
var args = Array.prototype.slice.call(arguments); | |
return __method__.apply(obj, __args__.concat(args)); | |
}; | |
}; | |
Function.prototype.curry = function (){ | |
var __method__ = this; | |
var __args__ = Array.prototype.slice.call(arguments); | |
return function (){ | |
var args = Array.prototype.slice.call(arguments); | |
return __method__.apply(this, __args__.concat(args)); | |
}; | |
}; | |
var GlobalActor = new function (){ | |
var call = function(callback){ | |
setTimeout(callback, 0); | |
}; | |
var Queue = function (){}; | |
Queue.prototype.enqueue = function (callback){ | |
call(callback); | |
}; | |
var AbstractActor = function(){ | |
this.queue = new Queue(); | |
}; | |
AbstractActor.prototype.act = function (){ | |
throw new Error('override actor'); | |
}; | |
AbstractActor.prototype.start = function (){ | |
this.queue.enqueue((function (){ | |
this.act.call(this, this); | |
}).bind(this)); | |
}; | |
AbstractActor.prototype.stop = function (){ | |
this.queue.enqueue = function (){ | |
// nop | |
}; | |
}; | |
this.AbstractActor = AbstractActor; | |
}; | |
var Actor = function (){ | |
this.messages = []; | |
}; | |
Actor.MESSAGE_BANG = 1; | |
Actor.MESSAGE_BANGBANG = 2; | |
Actor.MESSAGE_BANGQMARK = 3; | |
Actor.prototype = new GlobalActor.AbstractActor(); | |
Actor.prototype.loop = function(callback){ | |
var self = this; | |
var loop = function (){ | |
try { | |
callback.apply(self); | |
self.queue.enqueue(loop); | |
} catch(e) { | |
console.error(e); | |
} | |
}; | |
self.queue.enqueue(loop); | |
}; | |
Actor.prototype.performMessage = function(callback){ | |
// clear reply | |
this.reply = function (){}; | |
// dequeue message | |
var message = this.messages.shift(); | |
switch(message.type){ | |
case Actor.MESSAGE_BANG: | |
// no reply | |
return callback.call(this, message.parameter, message.sender); | |
case Actor.MESSAGE_BANGBANG: | |
// reply async | |
this.reply = function(value){ | |
var partialFunction = message.partialFunction; | |
if(!partialFunction){ | |
partialFunction = function(k){return k}; | |
} | |
var partialResult = partialFunction(value); | |
var future = message.future; | |
this.queue.enqueue(future.callback.curry(partialResult)); | |
}; | |
return callback.call(this, message.parameter, message.sender); | |
case Actor.MESSAGE_BANGQMARK: | |
throw new Error('not yet supported'); | |
} | |
}; | |
Actor.prototype.react = function(callback){ | |
var self = this; | |
return function (){ | |
if(0 < self.messages.length){ | |
self.performMessage.apply(self, [callback]); | |
} | |
}; | |
}; | |
Actor.prototype.receive = function(callback){ | |
throw new Error('not yet supported'); | |
}; | |
Actor.prototype['!'] = function (_parameter, _sender){ | |
this.messages.push({ | |
type: Actor.MESSAGE_BANG, | |
parameter: _parameter, | |
sender: _sender, | |
partialFunction: null, | |
future: null | |
}); | |
}; | |
Actor.prototype['!!'] = function(_parameter, _partialFunction, _sender){ | |
var __future__ = function(callback){ | |
arguments.callee.callback = callback; | |
}; | |
__future__.callback = null; | |
this.messages.push({ | |
type: Actor.MESSAGE_BANGBANG, | |
parameter: _parameter, | |
sender: _sender, | |
partialFunction: _partialFunction, | |
future: __future__ | |
}); | |
return __future__; | |
}; | |
Actor.prototype['!?'] = function(parameter){ | |
throw new Error('not yet supported'); | |
}; |
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
// | |
// example:: | |
// | |
var Foo = function (){}; | |
Foo.prototype = new Actor(); | |
Foo.prototype.act = function (actor){ | |
actor.loop(actor.react(function (message){ | |
console.log(message.msg); | |
actor.reply(message.msg); | |
})); | |
}; | |
var foo = new Foo(); | |
foo.start(); | |
foo['!']({msg: 'msg1'}); | |
var f = foo['!!']({msg: 'msg2'}, function (returnValue){ | |
return returnValue + ', partial'; | |
}); | |
f(function(result){ | |
console.log(result); | |
foo.stop(); | |
}); |
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
// | |
// PingPong: | |
// http://www.scala-lang.org/node/54 | |
// | |
var MSG_Start = 1; | |
var MSG_SendPing = 2; | |
var MSG_Pong = 3; | |
var MSG_Ping = 4; | |
var MSG_Stop = 5; | |
var Ping = function(count, pong) { | |
this.count = count; | |
this.pong = pong; | |
}; | |
Ping.prototype = new Actor(); | |
Ping.prototype.toString = function (){ | |
return 'Ping[count: ' + this.count + ', pong: ' + this.pong + ']'; | |
}; | |
Ping.prototype.act = function(actor){ | |
console.log('Ping initializing with count:' + this.count + ': ' + this.pong); | |
var pingsLeft = this.count; | |
actor.loop(actor.react(function(message, sender){ | |
switch(message){ | |
case MSG_Start: | |
console.log('Ping:start'); | |
this.pong['!'](MSG_Ping, this); | |
pingsLeft = pingsLeft - 1; | |
break; | |
case MSG_SendPing: | |
this.pong['!'](MSG_Ping, this); | |
pingsLeft = pingsLeft - 1; | |
break; | |
case MSG_Pong: | |
if(0 === (pingsLeft % 1000)){ | |
console.log('Ping pong from: ' + sender); | |
} | |
if(0 < pingsLeft){ | |
this['!'](MSG_SendPing, this); | |
} else { | |
console.log('Ping Stop'); | |
this.pong['!'](MSG_Stop, this); | |
this.stop(); | |
} | |
break; | |
} | |
})); | |
}; | |
var Pong = function (){}; | |
Pong.prototype = new Actor(); | |
Pong.prototype.toString = function (){ | |
return '[Pong]'; | |
}; | |
Pong.prototype.act = function(actor){ | |
var pongCount = 0; | |
actor.loop(actor.react(function(message, sender){ | |
switch(message){ | |
case MSG_Ping: | |
if(0 === (pongCount % 1000)){ | |
console.log('Pong ping:' + pongCount + ' from ' + sender); | |
} | |
sender['!'](MSG_Pong, this); | |
pongCount = pongCount + 1; | |
break; | |
case MSG_Stop: | |
console.log('Pong Stop'); | |
this.stop(); | |
break; | |
} | |
})); | |
}; | |
var pong = new Pong(); | |
var ping = new Ping(100000, pong); | |
ping.start(); | |
pong.start(); | |
ping['!'](MSG_Start); |
scalaのpingpongまでは動くようになった。
sender は外から指定するようにしてみた
setInterval -> setTimeout に変更してみた
partialFunction は function(k){return K} 的なものをデフォルトに
GlobalActorをどうしようか迷う...。(不要?)
setTimeoutでいいのかどうかは、未だに考え中
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
うーん。