Ajax.Requestオブジェクト(2)

引き続きprototype.jsのソースコードを読んでいこう。前回の部分まで読み進めると、AJAXアプリケーションで使用するリクエストを発行するコードが、以下のように記述できることがわかる。

var ajaxRequest = new Ajax.Request('<url>', '<options>')

それでは、このリクエストはこの後どのように処理されていくのだろうか。続いて、request()の処理内容を見ていこう。

 1013   request: function(url) {
 1014     this.url = url;
 1015     this.method = this.options.method;
 1016     var params = Object.clone(this.options.parameters);
           :
 1034     try {
           :
 1038       this.transport.open(this.method.toUpperCase(), this.url,
 1039         this.options.asynchronous);
           :
 1044       this.transport.onreadystatechange = this.onStateChange.bind(this);
           :
 1048       this.transport.send(this.body);
           :

new Ajax.Requestによりinitialize()中で、上記request()が実行される。この中では、まずrequestオブジェクトで使用するいくつかのパラメタをセットし、1034行目から始まるtryブロックを実行する。

1038行目ではXMLHttpObjectをオープンし、HTTPリクエストメソッドやリクエスト先URL、処理の同期非同期指定などの、指定されたオプションを渡している。

1044行目では、XMLHttpObjectのステータス変化をフックするイベントonreadystatechangeにonStateChange()をバインドしている。これにより、XMLHttpObjectのステータスが変化した際は、後述するonStateChange()が実行されることがわかる。

1048行目では、作成したXMLHttpObjectをsend()することで、リクエストを発行している。

それでは続いて、上記に挙げたonStateChange()、および、ここからコールされるrespondToReadyState()の実装を見ていこう。

 1060   onStateChange: function() {
 1061     var readyState = this.transport.readyState;
 1062     if (readyState > 1 && !((readyState == 4) && this._complete))
 1063       this.respondToReadyState(this.transport.readyState);
 1064   },
          :
 1106   respondToReadyState: function(readyState) {
 1107     var state = Ajax.Request.Events[readyState];
 1108     var transport = this.transport, json = this.evalJSON();
 1109 
 1110     if (state == 'Complete') {
 1111       try {
 1112         this._complete = true;
 1113         (this.options['on' + this.transport.status]
 1114          || this.options['on' + (this.success() ? 'Success' : 'Failure')]
 1115          || Prototype.emptyFunction)(transport, json);
 1116       } catch (e) {
 1117         this.dispatchException(e);
 1118       }
          :

onStateChange()ではXMLHttpObjectのreadyStateプロパティを取得し、これが1より大きければ(かつ、既に処理されていなければ(後述)) respondToReadyState()をコールしている。readyStateの値の意味については、前回紹介した

 1001 Ajax.Request.Events =
 1002   ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];

と対応している。この表現を借りれば、respondToReadyState()がコールされるのは、Loaded、つまり、読込み完了(以降)のステータスになったとき、ということになる。

respondToReadyState()では、XMLHttpObjectのステータスを確認し、'Complete' であれば1111行目以降のtryブロックを実行する。ここでは、1113行目から1115行目に渡っている、2つのthis.optionsおよびPrototype.emptyFuntionが順に評価される。1113行目のthis.options['on' + this.transport.status] によれば、this.options(つまり、new Ajax.Request()するときのoptions)としてonCompleteプロパティを定義しておけば、XMLHttpObjectが'Complete'となったときに、それが実行されることがイメージできるだろう。なお、1112行目で this._complete = true としているため、この処理は重複して行われることはない。

それでは、ここまで見てきたことを確認するために、ここで以下のコマンドをFirebugのプロンプトで入力し、AJAX処理を実行してみよう。なお、第9回の記事同様、prototype.jsを先に読み込んで(実行して)おく必要がある。

var complete = function(result) { alert(result.responseText); }
var ajaxRequest = new Ajax.Request('/', { 'method': 'GET', 'onComplete': complete })

上記のコマンドは、アクセス(ブラウザに表示)中のWebサーバのルートパスに設置されているコンテンツを取得し、これをダイアログ表示するものだ。取得した文字列をalertによりダイアログ表示するので、環境によっては巨大なダイアログに画面を占領されてしまうかもしれない。EnterでOKボタンを押すことによりダイアログが消去できるが、万一OKボタンが押せない状態になってしまった場合は、Alt+F4などの終了コマンドにより対処してほしい。

さて、実行内容を確認しておこう。上記で実行したのは、コールバックされる関数completeの定義と、Ajax.Requestの定義(発行)の2つだ。

順序が逆になるが、先に2行目のAjax.Requestの定義(発行)を確認しよう。ここでは、次のことを指定している。

  • リクエスト先URLとしてWebサーバのルートパスを指定
  • オプションとして、以下を指定
    • HTTPリクエストメソッドにGETを使用すること
    • readyStateがCompleteになったときのコールバック関数としてcompleteを使用すること

続いて1行目のcomplete関数の定義では、引数として渡されるresultオブジェクトのresponseTextプロパティをalertに渡していることがわかる。ここで渡されるresultオブジェクトには、(この時点では)リクエストの処理結果が入ることになる。

つまり、上記のコマンドを実行した際は、次の処理が実行される事になる。

  1. アクセス中のWebサーバのルートパスへGETリクエストを発行
  2. レスポンスを取得し、completeをコール
  3. resultオブジェクトからresponseTextを取得し、ダイアログ表示

誌面と実行環境の都合により、実践的なAJAXアプリケーションの事例を掲載するには及ばなかったことをご容赦いただきたい。しかしながら、これをフォームやリンクのonClick, onChangeなどのイベントでコールすることにより、非同期処理をおこなうAJAXアプリケーションが実装していけることはイメージしていただけるのではないだろうか。

さて、本連載では、5回に渡ってprototype.jsの中身を紹介してきた。prototype.jsはこの他にも数々の機能を提供しているが、これらの紹介は別の機会に譲ることとしたい。次回以降は、prototype.jsを使用している他のライブラリや、prototype.js以外のライブラリを見ながら、少しずつ実践的な内容へフォーカスしていこう。