prototype.jsのAjax.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 ヘッダに文字列設定して、そっちで取得するくらいでしょうか。
<