PeriodicalExecuterクラス
【抜粋】 var PeriodicalExecuter = Class.create(); PeriodicalExecuter.prototype = { initialize: function(callback, frequency) { this.callback = callback; this.frequency = frequency; this.currentlyExecuting = false; this.registerCallback(); }, registerCallback: function() { setInterval(this.onTimerEvent.bind(this), this.frequency * 1000); }, onTimerEvent: function() { if (!this.currentlyExecuting) { try { this.currentlyExecuting = true; this.callback(); } finally { this.currentlyExecuting = false; } } } }
典型的なprototype.jsのクラス作成方法により記述されています。Class.createメソッドでコンストラクタ関数を作成し、prototypeプロパティにinitializeメソッドと他のメソッドを追加しています。
使用方法は、関数と実行間隔秒数を指定してインスタンスを作成するだけです。以下のメソッドは外部から呼び出されることを想定していません。
initializeメソッド
指定された関数と実行間隔行数を保持し、実行状態フラグをfalse(実行していない)にしてregisterCallbackメソッドを呼び出します。
registerCallbackメソッド
setInterval関数により、onTimerEventメソッドを指定された実行間隔秒数ごとに、繰り返し実行させます。前述のFunction.bindメソッドにより、onTimerEventメソッドでthisが使用できます。
onTimerEventメソッド
実行状態フラグがfalse(実行していない)の場合に、フラグをtrue(実行中)にしてから、指定された関数を呼び出します。終了後に実行状態フラグをfalseに戻します。try〜finally節が使用されているのは、指定関数がエラー終了した場合でもフラグを戻すためです。
指定関数について
通常、指定関数には引数を指定できません。ですが、これも前述Function.bindメソッドを使用すれば指定することができます。
停止について。
※以下の内容はv1.4.0についてです。v1.5.0以降ではstopメソッドがあり、停止することが可能です。
PeriodicalExecuterで繰り返し実行すると、ページが変わるまで止められません。通常、setInterval関数が返却するタイマー識別子を使用して、clearInterval関数で停止できるのですが。タイマー識別子は捨てられてるみたいですし。*1なぜなのかはよく分からないです。。。
【例】 <html> <head> <title></title> <script language="javascript" src="prototype.js" charset="utf-8"></script> <script language="javascript"> <!-- var Test = Class.create(); Test.prototype = { initialize: function(a) { this.a = a; }, calc: function(b){ this.a *= b; document.getElementById("elem").innerHTML = this.a; } } var test = new Test(1); function init(){ new PeriodicalExecuter(test.calc.bind(test, 2), 3); } function stop(){ //止まりません^^; clearInterval(); clearTimeout(); } //--> </script> </head> <body onload="init();"> <div id="elem"> </div> <button onclick="stop();">STOP(しない><;)</button> </body> </html>
Testクラスのインスタンスtestのcalcメソッドを、Fucntion.bindメソッドを使用してPeriodicalExecuterに渡しています。PeriodicalExecuterのインスタンスは用途がないので取得していません。ページ読み込み後、3秒毎に数を倍にしてdiv要素に書き込みます。STOPボタンは意味なしです^^;
以下、改修案です。registerCallbackでタイマー識別子を取得、stopメソッドとrestartメソッドを追加しました。
【改修案】 PeriodicalExecuter.prototype = { initialize: function(callback, frequency) { this.callback = callback; this.frequency = frequency; this.currentlyExecuting = false; this.registerCallback(); }, registerCallback: function() { this.tid = setInterval(this.onTimerEvent.bind(this), this.frequency * 1000); }, restart: function() { if(this.tid) return; this.registerCallback(); }, stop: function() { if(!this.tid) return; clearInterval(this.tid); this.tid = null; } onTimerEvent: function() { if (!this.currentlyExecuting) { try { this.currentlyExecuting = true; this.callback(); } finally { this.currentlyExecuting = false; } } } }
【上記改修案を用いた場合の例】 <html> <head> <title></title> <script language="javascript" src="prototype.js" charset="utf-8"></script> <script language="javascript"> <!-- //オーバーライド PeriodicalExecuter.prototype = Object.extend(PeriodicalExecuter.prototype, { registerCallback: function() { this.tid = setInterval(this.onTimerEvent.bind(this), this.frequency * 1000); }, restart: function() { if(this.tid) return; this.registerCallback(); }, stop: function() { if(!this.tid) return; clearInterval(this.tid); this.tid = null; } }); var Test = Class.create(); Test.prototype = { initialize: function(a) { this.a = a; }, calc: function(b){ this.a *= b; document.getElementById("elem").innerHTML = this.a; } } var test = new Test(1); var pe; function init(){ pe = new PeriodicalExecuter(test.calc.bind(test, 2), 3); } //--> </script> </head> <body onload="init();"> <div id="elem"> </div> <button onclick="pe.restart();">RESTART</button> <button onclick="pe.stop();">STOP</button> </body> </html>