public
Last active

javascript Actor: scala's actor style

  • Download Gist
actor.js
JavaScript
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
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');
};
example_1.js
JavaScript
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
//
// 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();
});
pingpong.js
JavaScript
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
//
// 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でいいのかどうかは、未だに考え中

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.