Ajax.Requestのもったいない使い方

prototype.jsAjax.Requestを使って、JSON形式のデータを扱っていましたが、誤った(というかもったいない)使い方していたことに気が付きました。

Ajax.Requestを使ってデータをやり取りする際、下記の条件に当てはまるならば、無駄な処理が行われていると思われます。

  • Ajax.Requestのオプションで指定するonCompleteやonSuccess関数内で、XMLHttpRequestのresponseTextに対してevalを実施している。
  • Ajax呼び出し時に、サーバ側から返すContent-Typeに、"text/javascript"が含まれる。

なぜ無駄かというと、Ajax.Request内では処理完了時、レスポンスのContent-typeを判定し、"text/javascript"が含まれる場合、レスポンスの内容に対してevalを実行します。
(prototype.js ver1.4.0 にて700行目あたりから)

  evalResponse: function() {
    try {
      return eval(this.transport.responseText);
    } catch (e) {
      this.dispatchException(e);
    }
  },

  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);
    }

これにより、返却されたJavaScriptの内容を自動的に実行するような仕組みを実現していますが、このことを知らずにいると、onCompleteやonSuccessにてevalを再度実行するような処理を書いてしまい、結果的にevalが2度実行されることとなります。

データとそれを使った処理までが実行されるようなJavaScriptを書けば、Ajax.Request内のevalでサーバ側から受け取ったデータを扱えますが、単にJSON形式のデータの受け渡しでは、評価された値を取得することが出来ないのでそうも行きません。

データ量が少なければ、2度評価しても、それほど影響は無いと思いますが、大量のデータをいっぺんに取得するような場合には、十分な考慮が必要だと思います。

対処としては、Content-typeで"text/javascript"を含む文字を指定しないようにするか、あとは、X-JSON ヘッダに文字列設定して、そっちで取得するくらいでしょうか。


<>