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