My Photo

« 入力処理とセキュリティ | Main | クロスサイト・スクリプティング(XSS) »

September 01, 2013

jQueryで非同期な処理が終わるのを待つにはDeferredを使う

非同期な処理が終わるのを待って何かしたい場合、jQuery.Deferredというものを使うとできる。参考サイト2の説明がわかりやすい。
Deferred.promise()でPromiseオブジェクトを取得する。そしてDeferred.resolove()とするとPromise.done()でバインドした関数が、Deferred.reject()するとPromise.fail()した関数が実行される。Promise.then()は引数を複数取ってresolve時とreject時に実行される関数を一度にバインドできる。

deferred.resolve([args])
Deferredオブジェクトをresolved状態に移行し、指定した引数を
doneCallbacksコールバック関数に渡し、それを実行する。

deferred.reject([args])
Deferredオブジェクトをrejected状態に移行し、指定した引数を
failCallbacksコールバック関数に渡し、それを実行する。

deferred.then(doneFilter[, failFilter ][, progressFilter])
Deferredオブジェクトの各状態において呼び出すコールバック関数を登録する。

deferred.done(doneCallbacks[, doneCallbacks])
Deferredオブジェクトがresolved状態に移行したときに呼び出すコールバック関数を
登録する。

deferred.fail(failCallbacks[, failCallbacks ])
Deferredオブジェクトがrejected状態に移行したときに呼び出すコールバック関数を
登録する。

jQueryのAjax用メソッド(ajax(),post(),get())が返すjqXHRオブジェクトがDeferredインタフェースかPromiseインタフェースを実装しているらしい。どちらを実装してるのかよくわからないが、参考サイト3に

「Promiseは、ユーザがDeferredの状態の変更を防ぐための、Deferredのメソッドのサブセット(then,done,fail等)を提供する」

とあったので、あまり深く考えずにAjax関数の返り値に対してthen()などのメソッドでコールバック関数をバインドすると覚えておく。

追記:Deferred と Promise についてはちゃんと理解した(つもり)後に別記事を書いた。

処理が終わるのを待つなら同期処理のような気もする。使いどころがイマイチわからないが、例えば以下の例のようなことができる。
1つ目はHTML+Javascript、2つ目はサーバ側のphpのコード。

<?xml version="1.0" encoding="UTF-8"?>
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja">
<head>
<title>jQuery Deferred test</title>
<script type="text/javascript" src="./js/jquery-1.8.2.min.js"></script>
<script type="text/javascript">
$(function() {
	// イベントハンドラをバインド
	$("#btn01").on("click", function() {
		funcA();
	});
	
	// funcAを実行して、終わったら "[initital]" を書き加える
	funcA().done(function() {
		$("#displayArea").append("[initial]");
	});
});

// funcBを呼んでその戻り値を返すだけ。
function funcA() {
	return funcB();
}

// Ajax。レスポンスデータをdisplayAreaにセットする
function funcB() {
	var url = document.form.action;
	var data = {"mode": "time"};
	var $p = $.post(url, data, function(jdata) {
		$("#displayArea").text(jdata);
	},"json");
	return $p;
}
</script>
</head>
<body>
<form name="form" action="http://localhost/php/ajax.php" method="post">
<input type="button" id="btn01" name="btn01" value="btn01" />
<div id="displayArea" style="min-width: 100%; min-height: 100px; border: solid 2px"></div>
</form>
</body>
</html>
<?
header('Content-type: application/json');

$mode = $_REQUEST['mode'];
if ($mode === 'time') {
	print json_encode(strftime('%c'));
} else {
	print json_encode('success');
}
?>

この例では、最初の表示の時はAjaxでサーバから日時の文字列をJSON形式で取得し、その日時文字列をdiv要素のテキストとしてセットし、さらにdone()でバインドした関数(div要素のテキストに "[initial]" を追加する)を実行する。よって、画面には

<日付> <時間>[initial]

と表示される。
一方、ボタンを押した時は、サーバから時間を取得しdiv要素にセットするまでは同じだが、初期表示の時に実行されたdone()でバインドされた関数は実行されないので

<日付> <時間>

と日時だけ表示される。
resolove()は明示的に呼ばなくていい。post()が指定したコールバック関数が終了した後にresoveしているんだろう。

注意:
Deferredとは関係ないが、最初、クライアント側でpostの引数のコールバック関数が呼ばれず、原因がしばらくわからずに困った。
サーバ側のプログラムがjson_encode()を使わず文字列をそのまま返していたのを、json_encode()を使うようにしたら思ったように動作するようになった。クライアント側で受け取るデータを "json" と指定しているので、サーバ側のアプリもちゃんとJSON形式でデータを返すべきだった。
また、header()も呼んでいなかったので呼ぶようにした。なくても動くが、一応決まりとして呼んでおくべきだろう。

参考サイト:
1.Qrefy - jQuery日本語リファレンス
2.爆速でわかるjQuery.Deferred超入門 - Yahoo! JAPAN Tech Blog
3.Types | jQuery API Documentation

« 入力処理とセキュリティ | Main | クロスサイト・スクリプティング(XSS) »

JavaScript」カテゴリの記事

Comments

Post a comment

(Not displayed with comment.)

TrackBack

TrackBack URL for this entry:
http://app.cocolog-nifty.com/t/trackback/26461/58110057

Listed below are links to weblogs that reference jQueryで非同期な処理が終わるのを待つにはDeferredを使う:

« 入力処理とセキュリティ | Main | クロスサイト・スクリプティング(XSS) »

September 2017
Sun Mon Tue Wed Thu Fri Sat
          1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
無料ブログはココログ

日本blog村

  • にほんブログ村 IT技術ブログへ
  • にほんブログ村 アニメブログへ
  • にほんブログ村 サッカーブログ アルビレックス新潟へ

好きな音楽家

メモ

XI-Prof