こんばんは、martinです。タイトル通りなんですが、要は以下のようなことを簡単に実装したい。
foo.await(2000).say("Hello, ").await(1500).say("world !");
まぁfooという関数オブジェクトがあるとして、2秒後に「Hello, 」を出力して、そこで小休止、で1.5秒後に「world !」を出力すると。それをメソッドチェーンに拘ってやりたい。簡単な例を挙げてみます(うまく行かないサンプルです)。
var foo = function(){}; // 関数オブジェクトを作成 foo.prototype = { // プロトタイプを設定 say : function(s){ alert(s); return this; }, await : function(ms){ // ここで遅延処理をゴニョゴニョしたい // var _this = this; // setTimeout(function(){ // return _this; # まぁ、これはダメだけど、こんな感じでやれたらいい // }, ms); return this; } } var bar = new foo(); bar.await(2000).say("Hello"); // すぐに「Hello」が出力される
メソッドチェーンを実装するとなると、return this;をかまして、用意した関数オブジェクト自身を返すようにする必要があります。上のawait関数内で、setTimeoutを用いて遅延処理をしようと思いましたが、どうもうまい方法が思いつかず、とあるサイトで質問してみました 。すると、数分後(!)に返答があって、「それ、jQueryで出来るよっ!」と(そのコメントは今は消されたようなんですが) まぁ確かにそうなんですが(なのであえて.delayという単語を選んだ)何かすごい技があって、それで解決!みたいなことを期待していたわけです(jQueryを使わずに)。
2つ目に付いたコメントで、簡単に済ませる方法はなくて、何か別のキューシステム(待ち行列システム?)を用意する必要があるよ、と。やっぱりそうか。で、沢山ヒントを頂いたんで、自分なりに作ってみました。次のようなキューシステムを用意しました。
Queue = { entries : [], // 関数を登録する配列を用意 inprocess : null, enqueue : function(entry){ // 関数を登録する Queue.entries.push(entry); }, flush : function(){ // 配列に登録された関数群を一気に実行 if(Queue.inprocess) return; // 「待ち中」なら実行しない while (Queue.entries.length){ var entry = Queue.entries.shift(); // 配列の頭から取り出す(つまり登録順) if(entry.toString().indexOf('await:') !== -1){ // 「待ち」のマークを見つけたら var ms = Number(entry.split(':')[1]); Queue.inprocess = setTimeout(function(){ Queue.inprocess = null; Queue.flush(); // 指定時間後に処理を再開 }, ms); return; } entry(); // 取り出された関数を実行 } } }こいつを最初のサンプルに適用すると以下のような感じになります。
var foo = function(){}; foo.prototype = { say : function(s){ Queue.enqueue(function(){ // 配列に関数を登録 alert(s); }); Queue.flush(); return this; }, await : function(ms){ Queue.enqueue('await:' + ms); // 配列に「await:2000」みたいなマークを登録 return this; } }
自分のスキルではこの程度です キューシステムに頼らずに自己完結できる目から鱗な手法がないですかねぇ。
とりあえず動作サンプルを以下に。ボタンを押すと、
var bar = new foo(); bar.await(1500).say('Hello, ').await(2000).say('world!');ってのが実行されます。ここでは、ボタンを押して1.5秒後に「Hello, 」、更に2秒後に「world !」とボタンの右側に表示されます。
Comments