Ajax.Respondersオブジェクト
【抜粋】一部省略 Ajax.Responders = { responders: [], (省略) dispatch: function(callback, request, transport, json) { this.each(function(responder) { if (responder[callback] && typeof responder[callback] == 'function') { try { responder[callback].apply(responder, [request, transport, json]); } catch (e) {} } }); } }; Object.extend(Ajax.Responders, Enumerable); Ajax.Responders.register({ onCreate: function() { Ajax.activeRequestCount++; }, onComplete: function() { Ajax.activeRequestCount--; } });
Ajax.Respondersオブジェクトは、Ajax関連オブジェクトのイベント発生時の共通処理を担います。Ajax関連オブジェクトとは、具体的にはAjax.Requestクラス、Ajax.Updaterクラス、Ajax.PeriodicalUpdaterクラスのインスタンスすべてです。イベントと発生時期は以下です
イベント | 発生時期 |
---|---|
onCreate | インスタンス生成時(Ajax.Request.requestメソッド呼び出し時) |
onLoading | インスタンス生成の0.01秒後 |
onLoaded | データ送信時(ローカルでは発生しない) |
onInteractive | データ受信開始時 |
onComplete | 通信完了時(エラー時含む) |
onException | 例外発生時 |
0:Uninitializedは初期状態なので、prototype.jsだろうがなかろうが「onUninitialized」は発生しないと思います。*1onLoadedは発生します。ただし、ローカルだと発生しないようです。*2
onLoadingはXMLHttpRequestのreadyStateプロパティとは意味が異なっています。readyStateプロパティで1:Loadingは「openメソッド呼び出し後〜sendメソッド呼び出し前」を意味しますが、ここでは「インスタンス生成*3の0.01秒後」に発生します。このため、イベント発生時は必ずしもreadyStateの1:Loading状態ではありません。
【参考サイト】 http://jsgt.org/mt/archives/01/000278.html
【例】 <html> <head> <title></title> <script language="javascript" src="prototype.js" charset="utf-8"></script> <script language="javascript"> var str = ""; Ajax.Responders.register({ //各イベント発生時にstrへ書き込み onUninitialized: function() { str += "Uninitialized,"; }, onCreate: function() { str += "Create,"; }, onLoading: function() { str += "Loading,"; $("test").innerHTML = str; //onLoading発生時にstrを要素に書き込み }, onLoaded: function() { str += "Loaded,"; }, onInteractive: function() { str += "Interactive,"; }, onComplete: function() { str += "Complete,"; } }); function test(){ //Ajaxで'test.txt'を読み込んでalert表示 var req = new Ajax.Request( 'test.txt', { method: 'get', onComplete:function(_req){ alert(_req.responseText); } }); } </script> </head> <body> <div id="test"></div> <button onclick="test();">TEST</button> </body> </html>
registerは後述のメソッドでイベント発生時の処理を設定します。TESTボタンを押すと、test.txtの内容がalertで表示された後、画面に以下の文字列が表示されます。
【上記例の実行結果(ローカル)】 Create,Interactive,Complete,Loading,
場合によっては違う結果になるかもしれませんが、ローカルで実行すると高い確率でこうなると思います。つまり、インスタンス生成後0.01秒以内に処理が終了、その後Loadingイベントが発生していることになります。onLaodingはちょっと扱いにくいですね・・・。
Webサーバ経由で実行すると以下のようになります。(要素への書き込みはonCompleteで行います)
【上記例の実行結果(Webサーバ経由)】※Loadingの位置は後ろにずれる場合あり Create,Loading,Loaded,Interactive,Complete,
Loadedが表示されます。Loadingの位置は後ろにずれる場合があります。
onreadysatechange時のreadyStateの値を調査したところ、1>1>2>3>4となっていました。なぜ1:Loadingが2回発生するのかはわかりません。。。*4 proptotype.jsはこれを嫌い、1回にしようとしている気がします。ただ、0.01秒後というのはやっぱり意味が分かりませんね・・・。
onCreateとonCompleteにはデフォルトで Ajax.activeRequestCountをインクリメント・デクリメントする関数が設定されていますが、これを上書きする心配はありません。各処理は基本的に上書きされず、追加されるだけだからです。流れとしては
- registerメソッドによりrespondersプロパティ(配列)にオブジェクトを追加。
- dispatchメソッドによりresponders配列の各オブジェクトの該当メソッド(onComlete等)をすべて実行。
となっています。
各イベント時に実行される関数には引数が二つ渡されます。第一は通信で使用しているXMLHttpRequestオブジェクト、第二は受信データのヘッダにX-JSONという名前のものがあった場合に、その値をeval関数で実行して作成したオブジェクトが渡されます。jsonについてはAjax.Request.evalJSONメソッドで述べたいと思います。(jsonデータは「({name:value,...})」のように全体を括弧で括ったりしないと扱えません。後述。)
余談ですが、Ajax.RequestのonCompleteはそのオブジェクトだけの処理となります。
説明が前後してまった部分がありますが、各メンバの解読に移ります。
respondersプロパティ
【抜粋】
responders: [],
空のArrayオブジェクト(配列)です。以降のメソッドによりオブジェクトが登録・削除されます。
_eachメソッド
【抜粋】 _each: function(iterator) { this.responders._each(iterator); },
Ajax.Respondersオブジェクトはは後でEnumerableクラスを継承しています。_eachメソッドはEnumerableクラスの抽象メソッドの実装です。respondersプロパティの各オブジェクトをiteratorにより処理することができます。
ただ、responders(Arrayオブジェクト)のeachメソッドでなく、_eachメソッドを使用している理由がよく分かりません。処理は変わりませんけど。ルール上、eachメソッドを使ったほうがいいような。
registerメソッド
【抜粋】 register: function(responderToAdd) { if (!this.include(responderToAdd)) this.responders.push(responderToAdd); },
引数のオブジェクトresponderToAddが、respondersプロパティに含まれていない場合、これに追加します。responderToAddは前述したイベント名のメソッドを一つ以上持ちます
unregisterメソッド
【抜粋】 unregister: function(responderToRemove) { this.responders = this.responders.without(responderToRemove); },
前述registerメソッドの逆。引数のオブジェクトresponderToRemoveをwithoutメソッドによりthis.respondersより取り除きます。
dispatchメソッド
【抜粋】 dispatch: function(callback, request, transport, json) { this.each(function(responder) { if (responder[callback] && typeof responder[callback] == 'function') { try { responder[callback].apply(responder, [request, transport, json]); } catch (e) {} } }); }
自オブジェクトthisの、respondersプロパティの各オブジェクトについて、引数callbackと同名のメソッド(=関数)があった場合、引数request, transport, jsonを引き継いで実行します。・・・ただ、ここでapply関数を使用する必要性はない気がするのですが。この場合は以下でも変わらないような?
【参考】apply関数を使用しない場合 responder[callback](request, transport, json);
エラーchatch時は何もせずに処理を続行しています。
dispatchメソッドはAjax.Requestクラス内で使用されます。プログラマが呼び出すケースはほとんどないと思います。
【例】 <html> <head> <title></title> <script language="javascript" src="prototype.js" charset="utf-8"></script> <script language="javascript"> var str = ""; var rsp1 = { onCreate : function() { str += "Create1,"; } }; var rsp2 = { onCreate : function() { str += "Create2,"; } }; var rsp3 = { onCreate : function() { str += "Create3,"; } }; Ajax.Responders.register(rsp1); //rsp1登録 Ajax.Responders.register(rsp2); //rsp2登録 Ajax.Responders.register(rsp3); //rsp3登録 Ajax.Responders.unregister(rsp2); //rsp2削除 function test(){ var req = new Ajax.Request( 'test.txt', { method: 'get', onComplete:function(_req){ alert(_req.responseText); $("test").innerHTML = str; //要素に書き込み } }); } </script> </head> <body> <div id="test"></div> <button onclick="test();">TEST</button> </body> </html>
【上記例の実行結果】 Create1,Create3,
Enumerableクラスの継承
【抜粋】
Object.extend(Ajax.Responders, Enumerable);
Enumerableクラスを継承しています。
デフォルト処理の設定
【抜粋】 Ajax.Responders.register({ onCreate: function() { Ajax.activeRequestCount++; }, onComplete: function() { Ajax.activeRequestCount--; } });
onCreate時にAjax.activeRequestCountをインクリメント、onComplete時にデクリメントしています。前述したように、これが上書きされることはありません。