$.when()
で複数の非同期処理を行って、.done()
で結果を受け取ってその後の処理をするのですが、特に非同期処理がAjaxの時、$.when()
の.done()
でAjaxで取得できた値を受け取って加工するのではなく、Ajax側で取得したデータを加工して$.when()
のdone()
に渡したい時のやりかたのメモ。
例
var foo = $.ajax({ url : "foo_API_URL", // {"text": "foo"} が返ってくるとする type : "GET", dataType : "json", data: data }).done(function(res) { var resText = res.text; // "foo" // ↓ 加工済みのデータを返したい return resText }); var bar = function() { return 'bar'; }; $.when(foo, bar()).done(function(res1, res2){ // fooはAjaxが完了した段階で値が返されるので // res1は resText ではなく、resになる console.log(res1); // -> Object {text: "foo"} console.log(res2); // -> "bar" });
jQuery.Deferredを使う
fooはajaxなのでjqXHR
が返され、Ajaxが完了した時に内部的にDeferred.resolve()
が実行されている様です。
なので、$.ajax.done内でreturnをしてもその前にDeferred.resolve()
で取得した結果が返されてしまっているのだと思います。
であれば、明示的にDeferredオブジェクトを返す関数を呼び出すようにして、Deferred.resolve()
で加工済みの値を返すようにすれば、実現できそうです。
var foo = function() { var defer = $.Deferred(); $.ajax({ url : "foo_API_URL", // {"text": "foo"} が返ってくるとする type : "GET", dataType : "json", data: data }).done(function(res) { var resText = res.text; // foo // defer.resolve() を使って値を返す defer.resolve(resText); }); // Deferred オブジェクトを返す return defer.promise(this); }; var bar = function() { return 'bar'; }; // fooは関数になっているので、呼び出し方が変わる $.when(foo(), bar()).done(function(res1, res2){ // fooはDeferredオブジェクトが返されるので // res1は defer.resolve( resText )の resText が渡される console.log(res1); // -> "foo" console.log(res2); // -> "bar" });
感想
Ajaxのデータを加工して渡したい場合は関数にしなければならないので、$.when()での呼び出し方が変わってしまいます。
$.when()内での呼び出し方が () が有るものと、無いものができて少しキモチワルイかもしれません。
僕は引数に直接ajaxを入れて、()無しで$.when()の中に入れると、どのタイミングで引数のAjaxが実行されてるのか見通しが悪い用に感じ、また()有りと無しの引数が共存してるのに違和感があったので、jQuery.Deferredを使うパターンでajaxそのままで済む時も一旦ajaxをラップした関数にしてreturn defer.promise(this);
を使い$.when()内で呼び出すの処理は()有りで統一しています。一度関数を呼び出す分少し無駄な処理をしていると思いますが。
[参考]
JavaScript: The Good Parts ―「良いパーツ」によるベストプラクティス
- 作者: Douglas Crockford,水野貴明
- 出版社/メーカー: オライリージャパン
- 発売日: 2008/12/22
- メディア: 大型本
- 購入: 94人 クリック: 1,643回
- この商品を含むブログ (189件) を見る