Connection Manager(その2)
朝の続きで、YUIのConnection Managerのトップページを概観する。
CAllbackオブジェクトとScope
「this」という予約語は、相当に厄介な代物に思われる。
thisやsuperといった予約語は、Javaでは当たり前に使われるが、JavaScritptはそもそも(ページスコープ内での)インスタンスを記述するもの。
要するに全てがthis。
関数系やイベントが全て、インスタンスと考えると、thisは「それを含む関数や、変数」を表すと考えるのが普通。
ここでは、success、failure時のハンドラに、オブジェクト・リテラルで記述された変数(これまたオブジェクト)を利用する際には、thisという予約語が正常に働かないよと書いてある。
この場合に、thisを正常に「このインスタンス」と認識させるには、callbackオブジェクト内に、
scope: ハンドラー・オブジェクト
と記載しなさいとのこと。
var AjaxObject = { // 通信成功時のコークバックのハンドラ handleSuccess:function(o){ // 以下のthis.processResult()は、AjaxObject..processResult() // を意味する。 // and passes the response object o to AjaxObject's // processResult member. this.processResult(o); }, // 通信失敗時のコークバックのハンドラ handleFailure:function(o){ }, // 通信成功時の処理を記述する processResult:function(o){ }, // XMLHttpRequestの送信(パラメータnew=1&old=2) startRequest:function() { YAHOO.util.Connect.asyncRequest('POST', 'php/post.php', callback, "new=1&old=2"); } }; /* * Callbackオブジェクト */ var callback = { // 通信成功時には、AjaxObjectのhandleSuccessにレスポンスオブジェクトを渡す。 success:AjaxObject.handleSuccess, // 通信失敗時には、AjaxObjectのhandlehandleFailureにレスポンスオブジェクトを渡す。 failure:AjaxObject.handleFailure, // このscope属性を指定するのが重要 scope: AjaxObject }; // XMLHttpRequestのSend AjaxObject.startRequest();
Callbackオブジェクトとタイムアウト
コールバックオブジェクトに、以下のように記述すると、通信タイムアウトの設定ができる。
var callback = { success: function(o) {/*success handler code*/}, failure: function(o) {/*failure handler code*/}, timeout: 5000, argument: [argument1, argument2, argument3] }
このうちの
timeout: 5000,
がそれで、単位はmillisec。この場合には、5sec待って、responseが無ければ、通信は中止(abort)されて、Failureのコールバックハンドラに処理が移る。
通信に成功した場合のresponseオブジェクトのプロパティー
通信がsuccessで終了した場合、レスポンスオブジェクトには、以下のプロパティーが設定されて、sucess時のハンドラに渡される。
プロパティー | 意味 | |
tId | 一意のインクリメントされたトランザクション(通信処理)ID | |
status | HTTPのステータスコード | |
statusText | HTTPのステータスコードに対応したメッセージ | |
getResponseHeader[label] | 指定されたヘッダーラベルの値(String) | |
getAllResponseHeaders | HTTPヘッダー全体。ラベル−値のペアは、"\n"で区切られている。 | |
responseText | サーバーレスポンス(String:平文のテキスト) | |
responseXML | サーバーのレスポンス(XMLドキュメント) | |
argument | ユーザー定義の引数、もしくは、コールバック関数に定義された引数 |
通信に失敗した場合のresponseオブジェクトのプロパティー
通信エラーになったり、タイムアウトになったときにも、Connection Managerはできるだけの情報を得ようとする。以下はエラー、タイムアウト(aborted)の場合で最低保障されているプロパティー値。
1.通信エラーの場合
プロパティー | 意味 | |
tId | 一意のインクリメントされたトランザクション(通信処理)ID | |
status | 0 | |
statusText | "communication failure" | |
argument | ユーザー定義の引数、もしくは、コールバック関数に定義された引数 |
2.タイムアウトの場合
プロパティー | 意味 | |
tId | 一意のインクリメントされたトランザクション(通信処理)ID | |
status | -1 | |
statusText | "transaction aborted" | |
argument | ユーザー定義の引数、もしくは、コールバック関数に定義された引数 |
Uploadの場合
(ここは具体的なイメージがわかない。後でサンプルが出てくるか??)
iframe(インラインフレーム)からuploadが行われた場合、Connection Managerは、レスポンスのステータスコードで成功か、失敗かの判断ができない。
その代わりに、CallbackのUploadハンドラーは、文字列で、インラインフレーム用にbodyタグで括られたテキストを受け取ることができる。このレスポンスはどんなタグ(Markup)やScriptを含むことができるから、カスタマイズをすることができる。
プロパティー | 意味 | |
tId | 一意のインクリメントされたトランザクション(通信処理)ID | |
responseText | Bodyタグで括られたiframe内に示すテキスト(String:平文) | |
responseXML | サーバーのレスポンス(XMLドキュメント) | |
argument | ユーザー定義の引数、もしくは、コールバック関数に定義された引数 |
FormsとSetForm
Connection Managerは、FromをsetFormメソッドによって、GETでもPOSTでも送ることができる。
通信トランザクションを開始する前に(つまり、XMLHttpRequestを送信する前に)、このメソッドを呼ぶことで、Formの内容からGETのQuery文字列や、Formメッセージを生成して、指定したURLに送ることができる。
この機能を使うには、文字列のname属性値を定義しなくてはならない。
こんな風に書く。
//引数のformIdはidか、HTML Form、もしくはFormオブジェクトのname属性。 var formObject = document.getElementById('aForm'); YAHOO.util.Connect.setForm(formObject); //この例では、POSTでおくっているが、GETの時は第1引数を'GET'にする。 var cObj = YAHOO.util.Connect.asyncRequest('POST', 'http://www.yahoo.com', callback);
FormにFileをアップロードする際(input type="file"が含まれるとき)には、setFormの第2引数にtrueを指定する。
Fileのアップロードが終わったら、Callbackオブジェクトのuploadメソッド(その1の最後に記述したもの)が呼ばれる。
こんな感じ。
var formObject = document.getElementById('aForm'); // 第2引数がtrueというのは、FileをUploadすることを意味する。 YAHOO.util.Connect.setForm(formObject, true); var cObj = YAHOO.util.Connect.asyncRequest('POST', 'http://www.yahoo.com', callback);
IEからSSLを通してUploadするときには、setFormの第3引数にもtrueを設定する。
YAHOO.util.Connect.setForm(formObject, true, trure);
トランザクションとカスタムイベント
Connection ManagerはYAHOO.util.CustomEventをimplementsしている。(JavaではImplementsは意味のある言葉だが、ここでの意味は、同様の機能を実装していると捉えるのが妥当(??)
以下がその一覧。
カスタムイベント | 意味 |
startEvent | トランザクションがスタートした際に発火し、subscribeされているハンドラにトランザクションIDを送る |
completeEvent | トランザクションが完了した際に発火し、subscribeされているハンドラにトランザクションIDを送る |
successEvent | トランザクション・レスポンスがが完了し、HTTPステータスコードが200番台(正常系)の時に発火する。subscribeされているハンドラにレスポンスオブジェクトを送る。 |
(callback success handlerに似ている。[注意]ファイル・アップロードのトランザクションでは発火しない)|
failureEvent | トランザクション・レスポンスがが完了し、HTTPステータスコードが400/500番台(異常系)の時、もしくは、ステータスコードが不明な場合に発火する。subscribeされているハンドラにレスポンスオブジェクトを送る。 |
(callback failure handlerに似ている。)|
uploadEvent | File Uploadが完了した場合に発火する。File Uploadトランザクションの時だけに発火するもので、successEvent、failureEventに代わるもの。 |
subscribeされているハンドラにレスポンスオブジェクトを送る。
(callback upload handlerに似ている。)|
abortEvent | トランザクションタイムアウトによる中断(abort)か、明示的にYAHOO.util.Connect.abort()によって通信を中断したときに発火する |
カスタムイベントは、Global levalで(ページスコープ内の全てのトランザクションに対して)発火するが、トリガーは個々のトランザクションレベルでひかれる。
これだと複数のトランザクションを記述する際に都合が悪い。
この解決策として、以下のように、Globalなハンドラを用意して、そのハンドラをカスタムイベントにsubscribeする例が挙げられている。
こんな感じ。
// YAHOO.util.ConnectのAlias var YUC = YAHOO.util.Connect; /* * Connection managerのカスタムイベントの全てのハンドラを定義する。 * * イベントハンドラーはオブジェクトメンバーではなく、グローバルなfunctionにする * subscribeの際にscope引数を無視すること。 */ var handleEvents = { // globaleventなので、Handler関数のどこでも"this"を使ったらだめ。 start:function(eventType, args){ // startEvent発火時の処理を記述。 // eventTypeはstartEvent(文字列で指定)。argは配列。 // responseオブジェクト配列の第一引数に入る。responseオブジェクトは // ただ1つトランザクションIDを持つ // property: tId (the transaction ID). }, complete:function(eventType, args){ // do something when completeEvent fires. }, success:function(eventType, args){ // do something when successEvent fires. }, failure:function(eventType, args){ // do something when failureEvent fires. }, // Define this event handler for file upload transactions *only*. // This handler will not be used for any other transaction cases. upload:function(eventType, args){ // do something when uploadEvent fires. }, abort:function(eventType, args){ // do something when abortEvent fires. } }; /* * この例は、Connection Manager Levelで発火するカスタムイベントをsubscribe * する例。 * subscribeされると、全てのトランザクションで発火する。 */ // globaleventなので、Handler関数のどこでも"this"を使ったらだめ。 YUC.startEvent.subscribe(handleEvents.start, handleEvents); YUC.completeEvent.subscribe(handleEvents.complete, handleEvents); // File Uploadの時は発火しない。 YUC.successEvent.subscribe(handleEvents.success, handleEvents); // File Uploadの時は発火しない。 YUC.failureEvent.subscribe(handleEvents.failure, handleEvents); // FileUploadのsuccess, failureのときだけ発火。 YUC.uploadEvent.subscribe(handleEvents.upload, handleEvents); YUC.abortEvent.subscribe(handleEvents.abort, handleEvents); /* (注) * asyncRequest()において、callbackオブジェクトを指定する必要はない。 * (すでに、Connection Manager Levelでsubscribeされているので) * callbackオブジェクトもカスタムイベントレベルで書かれているので必要ない。 * (書いてもいいが、冗長なコードになる。) */ var transaction = YUC.asyncRequest('GET', sUrl, { timeout: 3000 });
Global Custom Eventではなくて、ある特定のトランザクションにカスタムイベントを仕込みたい場合には、callbackオブジェクトに以下のように、メンバー変数*customevents*を指定して、callback.customeventsを定義する。
var callback = { customevents:{ onStart:function(eventType, args) { // eventType は "startEvent". // args[0].tId は整数のtransaction ID. // args[1] は <code>callback.argument</code>, //(ただし、callback.argumentが定義されていた場合) }, onComplete:function(eventType, args) { // eventType は "completeEvent". // args[0].tId は整数のtransaction ID. // args[1] は <code>callback.argument</code>, //(ただし、callback.argumentが定義されていた場合) }, onSuccess:function(eventType, args) { /* * eventType は "successEvent". * args[0] は the response object。 * プロパティーは以下。 * * args[0].tId * args[0].status * args[0].statusText * args[0].getResponseHeader[ ] * args[0].getAllResponseHeaders * args[0].responseText * args[0].responseXML * args[0].argument */ }, onFailure:function(eventType, args) { // eventType は "failureEvent". // args[0] は response object. }, onUpload:function(eventType, args) { // eventType は "uploadEvent". // args[0] は response object. }, onAbort:function(eventType, args) { // eventType は "abortEvent". // args[0].tId は整数のtransaction ID. // args[1] は <code>callback.argument</code>, //(ただし、callback.argumentが定義されていた場合) }, }, }
また、以下のような例が挙げられている。
var handleEvent = { start:function(eventType, args){ // do something when startEvent fires. }, complete:function(eventType, args){ // do something when completeEvent fires. }, success:function(eventType, args){ // do something when successEvent fires. }, failure:function(eventType, args){ // do something when failureEvent fires. }, abort:function(eventType, args){ // do something when abortEvent fires. } }; // ハンドラー関数の中で"this"を使うには、scopeプロパティーにhandleEventを指定すること。 // scopeプロパティーをomitすると、Globalなカスタム・カスタムイベントとなる。 // 上で定義されたカスタムイベントを使う。 var callback = { customevents:{ onStart:handleEvent.start, onComplete:handleEvent.complete, onSuccess:handleEvent.success, onFailure:handleEvent.failure, onAbort:handleEvent.abort }, scope:handleEvent, argument:["foo","bar","baz"] }; var transaction = YAHOO.util.Connect.asyncRequest('GET', sUrl, callback);
Signature
とりあえず、飛ばし。使うときが来たら参照。
Transactionステータス
var cObj = YAHOO.util.Connect.asyncRequest('GET','http://www.yahoo.com',callback); var callStatus = YAHOO.util.Connect.isCallInProgress(cObj);
のように、YAHOO.util.Connect.isCallInProgress()を使うと、非同期通信が処理中なのかを判断できる。(処理中の場合にはtrue)が返る。