概要
Webページには、JavaScript等のプログラムで、動的に一部を書き換えるAjaxと呼ばれる技術があります。
ここでは、私がJavaScriptのライブラリであるJQueryを使って動的にWebページを変更したときに気づいたことについて書きます。
JQueryのAjax
JQueryでAjaxを実現するためには、$.ajax()や$.getJson()を始めとするAjax用の関数を使う方法があります。
$.ajax()は、任意のURIにリクエストを送り、そのレスポンスを受け取って処理をするというものです。
$.getJson()は、任意のURIにリクエストを送り、レスポンスとしてJSON形式のデータを受け取るというものです。
非同期で動作する
実は、JQueryのこれらの関数は、非同期で動作します。
なので、下記のプログラムでは、正しく動作しないことがあります。
function() {
var jsons = [];
for (int i = 0; i < 100; i++) {
$.getJson('取得したいJSONのURI')
.done(function(json) {
jsons.push(json);
});
}
// jsonsに対する処理
// jsonsのサイズが100とは限らない
jsons.length;
}
プログラムの内容は、JSONファイルを100個受け取り、その結果を毎回jsonsという配列に入れて、最後の何かの処理をするというものです。
実は、このプログラムでは、jsonsに対する処理と書かれている部分で、jsonsのサイズが100になっているかというとそうではありません。
なぜなら、$.ajax()や$.getJson()は非同期に動作するからです。
そのため、$.getJson()が100回分の実行を完了させる前に、jsonsに対する処理を開始してしまいます。
対処法1
対処法として、あげられるのは非同期で動作しているものを同期して動作するようにすることです。
function() {
var jsons = [];
for (int i = 0; i < 100; i++) {
$.ajaxSetup({ async: false });
$.getJson('取得したいJSONのURI')
.done(function(json) {
jsons.push(json);
$.ajaxSetup({ async: true });
});
}
// jsonsに対する処理
// jsonsのサイズは100
jsons.length
}
$.ajaxSetup()関数では、オプションでAjax用の関数の非同期・同期の切り替えをすることができます。なので、このように書き換えるとその後の処理の部分でjsonsのサイズは100となります。
しかしこの場合、非同期で動作しないようにしているため、$.getJson()の処理を行なっている間は、そこでJavaScriptの処理が止まってしまいます。
対処法2
そもそも、$.getJson()関数の外で処理を行なっているのが悪いということで、JSONの処理を$.getJson()関数でJSONの取得が成功したときに呼び出される関数の中でやってしまうという方法もあります。
function() {
for (int i = 0; i < 100; i++) {
$.getJson('取得したいJSONのURI')
.done(function(json) {
// jsonに対する処理
});
}
}
私の場合
今回の例では、JQueryのAjaxの関数を複数呼び出したときに非同期で呼び出されるために、必要な処理が終わらないまま、次の処理に進んでしまうという例を出しました。
私の場合は、複数の$.getJson()関数から値を受け取ってそれを処理して、関数の戻り値とするというプログラムを書こうと思ったときに、関数の戻り値がおかしいということに気づいて、どうしてそうなったのか調べたことから、今回の気をつけるべきところを発見しました。
ちなみに、JQueryの非同期について調べてみると、$.when()などを使うといいということがありましたが、そもそも$.when()は複数のAjax用の関数を使ったときに、それらの処理が全て終わったら次の処理をするもので、for文を使った、何回呼び出すかわからない場合は使いにくいものでした。
なので、私は今回の対処法として、そもそも関数の戻り値として結果を返すのではなく、前処理を除いて、Ajax用の関数の中で全て完結するように処理を書き換えました。
(今回の対処法2の方法です。)
まとめ
JQueryを使ってAjaxを実現しようとすると、とても簡単にできます。
しかし、このような仕様のため通常のJavaScriptのプログラムと同じように書いてはいけない部分などもあります。
なので、このことを心に留めておきながら、しっかり関数仕様を考えて処理をかけるようになっていきたいと思います。