Ajax.Requestクラス(2)

respondToReadyStateメソッド

【抜粋】
  respondToReadyState: function(readyState) {
    var event = Ajax.Request.Events[readyState];
    var transport = this.transport, json = this.evalJSON();

    if (event == 'Complete') {
      try {
        (this.options['on' + this.transport.status]
         || this.options['on' + (this.responseIsSuccess() ? 'Success' : 'Failure')]
         || Prototype.emptyFunction)(transport, json);
      } catch (e) {
        this.dispatchException(e);
      }

      if ((this.header('Content-type') || '').match(/^text\/javascript/i))
        this.evalResponse();
    }

    try {
      (this.options['on' + event] || Prototype.emptyFunction)(transport, json);
      Ajax.Responders.dispatch('on' + event, this, transport, json);
    } catch (e) {
      this.dispatchException(e);
    }

    /* Avoid memory leak in MSIE: clean up the oncomplete event handler */
    if (event == 'Complete')
      this.transport.onreadystatechange = Prototype.emptyFunction;
  },

respondToReadyStateメソッドを呼び出すのは、onStateChangeメソッドか、requestメソッド内ののsetTimeout関数です。つまり、1:Loading以外のonreadystatechange時と、インスタンス作成の0.01秒後になります。

引数readyStateには、その時点のXMLHttpRequestのreadyStateプロパティの値が設定されます。ただし、setTimeout関数から呼ばれた場合はreadyStateプロパティの値にかかわらず、1(Loading)が設定されます。

変数eventに引数に対応するイベントを表す文字列、変数transportに自オブジェクトのXMLHttpRequest、変数jsonに、前述evalJSONメソッドを使用して、X-JSONヘッダの実行結果を格納します。*1

変数eventが'Complete'(通信完了)のとき、まずoptionsプロパティのオブジェクトに、"on"+(その時点のXMLHttpRequestのstatusプロパティの値(=HTTPステータスコード))に該当するメソッドがあればそれを実行します。該当メソッドがない場合、Ajax.Baseから継承したresponseIsSuccessメソッドにより通信の成否を判定し、成功時にonSuccessメソッド、失敗時にonFailureメソッドがあれば実行します。これもない場合はPrototype.emptyFunctionを実行します(つまり何もしない)。引数はいずれも変数transportと変数jsonを使用します。try節でエラーが発生した場合はdispatchExceptionメソッドを実行します。

Content-typeヘッダーが「text/javascript」から始まる場合(大小文字不問)、前述evalResponseメソッドを使用して受信したデータをevalで処理します。ただし、返却値は取得していません。

以降は通信完了でなくても実行されます。まずoptionsプロパティのオブジェクトにに"on"+(変数eventの値)に該当するメソッドがあれば実行し、なければ何もしません。次にAjax.Responders.dispatchメソッドにより、Ajax.Respondersに登録されたオブジェクトの"on"+(変数eventの値)に該当するメソッドをすべて実行します。つまり、インスタンス個別処理の後に、Ajax関連クラス共通処理を行います。ここも、try節でエラーが発生した場合はdispatchExceptionメソッドを呼び出しています。

最後に、IEでのメモリーリークを回避するためのコードが書かれています。変数eventが"Complete"であればXMLHttpRequestのonreadystatechangeプロパティにPrototype.emptyFunction(無処理関数)を設定しています。申し訳ありませんが、力不足で詳しいことは分かりません。以下は推測です。前述requestメソッドでは以下のようになっています。

【参考】
this.transport.onreadystatechange = this.onStateChange.bind(this);

Functionクラスの拡張メソッドbindを使用して登録しています。ここでのthisへの参照が、通信完了後も自オブジェクトを存続させている、つまり、ガベージコレクトの対象外にしているのではないかと。(ただ、だとすると、IEだけの現象というのも変な気がするのですが・・・。)

参考となると思われるのは以下です。

【参考サイト】
MSDN:Understanding and Solving Internet Explorer Leak Patterns(英語)

上記ページで関連すると思われるのは、2.のClosuresに関する部分ではないかと思います。

dispatchExceptionメソッド

【抜粋】
  dispatchException: function(exception) {
    (this.options.onException || Prototype.emptyFunction)(this, exception);
    Ajax.Responders.dispatch('onException', this, exception);
  }

前述メソッドでエラー発生時に呼び出されるメソッド。引数exceptionはエラーオブジェクトです。optionsプロパティのオブジェクトのonExceptionメソッドと、Ajax.Respondersに登録されているオブジェクトのonExceptionメソッドをすべて呼び出します。その際の引数は自オブジェクトとエラーオブジェクトになります。

*1:ただし、素直には動いてくれない気がします。前述evalJSONメソッドを参照してください