JSDeferred with jQuery
ある案件でjQueryのgetJSONメソッドを使っていてgetJSONメソッドでは使いづらい部分があった。
この問題をなんとか解決できないかと調べてみたらJSDeferred(cho45.stfuawsc.com)という面白いJavaScriptのライブラリを見つけた。
jQueryのgetJSONメソッドの使いづらい点
-
エラーハンドリングができない
$.ajaxメソッドを使えばエラーハンドリングは可能であるが、読み込むデータがJSONの場合はgetJSONメソッドの方が簡潔にコードを記述できるのでgetJSONメソッドでエラーハンドリングができるようにしたい。 -
複数のJSONデータが必要な場合に非同期で処理をおこなうにはgetJSONメソッドをネストさせる必要がある。
例えば以下のようなコードになる。
$.getJSON("foo.json").next(function (foo_data) { $.getJSON("bar.json").next(function (bar_data) { // ここでfoo.jsonとbar.jsonを処理するコードを記述する。 }); });
JSDeferredは単独で使う事もできるがjQueryと組み合わせて使う事もでき、その場合にはgetJSONメソッドでエラーハンドルをおこなう事ができる。
また、非同期処理や並列処理もおこなう事ができる。
JSDeferred についての説明は本家のdoc/intro.html (Japanese)ページに載っているので、ここではおもにjQueryと組み合わせて使う場合について説明する。
JSDeferredをjQueryと組み合わせて使う場合はJSDeferredをダウンロードしてその中に含まれるjsdeferred.jquery.jsを読み込む必要がある。
通常はJSDeferredの関数を使う前に$.deferred.define()メソッドを呼び出してJSDeferredの関数をグローバル関数として公開する必要がある。
以下のリストは、JSDeferredの関数nextメソッドを呼び出した例である。
nextメソッドの引数として実行される関数は非同期で実行されるため、alert(1),alert(2)の順で実行される。
<script type="text/javascript" src="../js/jsdeferred.jquery.js"></script> <script type="text/javascript"> $.deferred.define(); next(function () { alert(2); }); alert(1); </script>
グローバル関数として公開したくない場合は$.deferred.define()メソッドを使わずにDeferredクラスのクラスメソッドとしてJSDeferredの関数をを呼び出してやれば良い。
その場合は以下のリストのようになる。
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script> <script type="text/javascript" src="../js/jsdeferred.jquery.js"></script> <script type="text/javascript"> Deferred.next(function () { alert(2); }); alert(1); </script>
また、jQueryを使わずに単独でJSDeferredを使う場合は以下のコードとなる。
<script type="text/javascript" src="../js/jsdeferred.js"></script> <script type="text/javascript"> Deferred.define(); next(function () { alert(2); }); alert(1); </script>
next関数のもう少し複雑な例を紹介する。
以下の例はnext関数の引数である関数の中で更にnext関数を実行した例である。
alert(1)からalert(5)の順で実行される。
next(function () { next(function () { alert(4); }).next(function () { alert(5); }); alert(2); }).next(function () { alert(3); }); alert(1);
wait関数を使って実行のタイミングを遅らせる事もできる。
wait関数の引数は秒単位で指定する。
elapsed引数にはミリ秒単位で実際の遅れ時間が返される。
ここでconsole.logメソッドはFireFoxのConsole API(Firefox 3とFirebugで始めるJavaScript開発:第2回 Firebugによるデバッグの基本,Console APIとその活用|gihyo.jp … 技術評論社)で用意されているメソッドです。(IE8でも使えるようである。)
wait(0.1).next(function (elapsed) { console.log(elapsed); //=> may be 90-110 }); console.log(1);
jQueryのgetJSONメソッドを使って複数のJSONファイルを非同期で処理する例を示す。
var results = []; next(function () { return $.getJSON("foo.json").next(function (data) { results.push(data); }); }). next(function () { return $.getJSON("bar.json").next(function (data) { results.push(data); }); }). next(function () { return $.getJSON("baz.json").next(function (data) { results.push(data); }); }). next(function () { alert(results); });
この処理は以下のように記述する事もできる。
var results = []; $.getJSON("foo.json").next(function (data) { results.push(data); }). next(function () { return $.getJSON("bar.json").next(function (data) { results.push(data); }); }). next(function () { return $.getJSON("baz.json").next(function (data) { results.push(data); }); }). next(function () { alert(results); });
parallel関数を使う事でもっと簡潔な記述をする事もできる。
parallel([ $.getJSON("foo.json"), $.getJSON("bar.json"), $.getJSON("baz.json") ]). next(function (results) { alert(results); });
parallel関数の引数として連想配列を使う事もできる。
parallel({ foo: $.getJSON("foo.json"), bar: $.getJSON("bar.json"), baz: $.getJSON("baz.json") }). next(function (results) { console.log(results.foo,results.bar,results.baz); });
loop関数を使う事もできる。
var wants = ["foo.json", "bar.json", "baz.json"]; var results = []; loop(wants.length, function (i) { return $.getJSON(wants[i]).next(function (data) { results.push(data); }); }). next(function () { alert(results); });
loop関数は以下のような引数の指定をする事もできる。
//=> loop 1 to 100 loop({begin:1, end:100, step:10}, function (n, o) { for (var i = 0; i < o.step; i++) { log(n+i); } });
JSONファイルが存在しないなのどajacの処理中にエラーが発生した場合の処理をerror関数にて記述できます。
next(function () { return $.getJSON("xxx.json").next(function (data) { // 成功した場合の処理 }); }). error(function (e) { alert(e); });
また、ajacの処理中にエラーが発生した場合に更に別のajaxの処理をおこなわせる等の複雑なエラーハンドリングを実行できます。
以下のコードではfoo.jsonまたはxxx.jsonの読み込みに失敗した場合に更にbar.jsonとbaz.jsonの読み込みを実行し成功した場合はalert(results)を実行し失敗した場合はalert(e)を実行します。
var results = []; $.getJSON("foo.json").next(function (data) { results.push(data); }). next(function () { return $.getJSON("xxx.json").next(function (data) { results.push(data); }); }). error(function (e) { return $.getJSON("bar.json").next(function (data) { results.push(data); }); }). next(function () { return $.getJSON("baz.json").next(function (data) { results.push(data); }); }). next(function () { alert(results); }). error(function (e) { alert(e); });