My Photo

« PHPメモ037:cURLでHTTPSなサイトにアクセスする | Main | jQueryのAjax関数はPromiseを実装したjqXHRを返す »

April 06, 2014

Deferredの復習

以前の関連する記事:jQueryで非同期な処理が終わるのを待つにはDeferredを使う

以前、jQueryのDeferredについての記事を書いて、大雑把には理解できた。
Deferredについて最もわかりやすい解説は参考1,2だと思う。概念的には決して難しくないのだが、Deferredのメソッドが各々どんな引数を取り、どんな値を返すのかとか、日本語のリファレンスを読んでも読み取れなかった(書いてなかった?)こととかがある。
そういうところをところをあらためてまとめた。

DeferredとPromise

Deferred.promise() が返すPromiseオブジェクトとは何か。以前の記事では参考3から下記の引用を示した。

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

で、それってつまりどういうことかというと、参考2から引用すると

.promise()という謎のメソッドが書かれていますが、これはDeferredオブジェクトから.resolve()や.reject()といった「状態を変更するメソッド」を取り除いたサブセットを返します。Deferredオブジェクトを直接返してしまうと、受け取った側で状態を変更できてしまい、非同期処理そのものが本当に終わったのかどうか保証が取れなくなります。これを防ぐため、.promise()によるクリーニング作業が必要になるわけです。

とのこと。つまり、Promiseオブジェクトは done() や fail() は持ってるけど resolve() や reject() は持っていない。なのでDeferredではなくPromiseを渡しておけば、渡した先で勝手に状態を変更されたりしなくて安心、ってこと。なるほど。

Diferred.then

Diferred の then() はdone() と fail() を一度にできて便利、というだけではなかった(jQuery1.8以降)。
done() と fail() が返す値は元のPromiseそのものである(Deferredではなくて?)。単純に return this している。
対して、then() は新たなPromiseを作成して返す。
また、then() に普通の関数を渡した場合とPromiseを返す関数を渡した場合も動作が違う。
これら違いについて下記の3つの関数を使って具体的に説明する。

/**
 * 2秒後にコンソールに出力する。必ずresolveする。
 * Promiseを返す。
 * @returns Promise
 */
function delayHello() {
	var d = new $.Deferred;
	setTimeout(function() {
		console.log('Hello!');
		d.resolve();
	}, 2000);
	return d.promise();
}

/**
 * 3秒後に(以下同文)
 */
function delayHello3000() {
	var d = new $.Deferred;
	setTimeout(function() {
		console.log('Hello!');
		d.resolve();
	}, 3000);
	return d.promise();
}

function hello() {
  console.log('Hello! sync');
}

下記は done() でメソッドチェーンを作成している。

delayHello()
.done(delayHello)
.done(delayHello);

この場合、2行目のdone() が返すのは最初の delayHello() が返すPromise(p1と呼ぶ)なので、2行目の done() も3行目の done() も同じp1に対する処理である。よって、p1がresolvedになると2行目の done() に渡された delayHello と 3行目 done() に渡された delayHello が間を置かずに続けて実行される。
consoleには以下のように出力される。

Hello!  ←2秒後
Hello!  ←4秒後
Hello!  ←4秒後

下記は then() に普通の関数を渡してメソッドチェーンを作成している。

delayHello()
.then(hello)
.then(hello);

then() に普通の関数を渡すと、then() が返すPromiseは前のPromiseとは異なるオブジェクトであるが、状態は引き継ぐ。上のコードでは最初の delayHello() が返すPromiseがresovedになると、2行目の then() が返すPromiseも直ちにresolvedとなり、結果として2行目の hello() と3行目の hello() は間を置かず続けて実行される。
consoleには以下のように出力される。

Hello!       ←2秒後
Hello! sync  ←4秒後
Hello! sync  ←4秒後

下記は then() にPromiseを返す関数を渡してメソッドチェーンを作成している。

delayHello()
.then(delayHello)
.then(delayHello);

2行目の then() が返すPromiseは(p3と呼ぶ)、最初の delayHello() が返すPromise(p1と呼ぶ)と2行目の then() に渡された delayHello() が返すPromise(p2と呼ぶ)の両方と関連のあるPromiseで、p1 と p2 の両方がresolovedになると p3 もresolvedになる。よって、3つのdelayHello() は2秒の間隔を開けて順番に実行される。
consoleには以下のように出力される。

Hello!  ←2秒後
Hello!  ←4秒後
Hello!  ←6秒後

結局、then() で処理を連結するとなにがうれしいかというと、「順番に非同期処理を行う」コードを「コールバック地獄」(参考2)にならず直感的に書けるということのようだ。
「処理を直列に連結」という説明がイメージ的にわかりやすい。

$.when

when() は引数に1個以上のオブジェクトを取り、渡されたオブジェクトをまとめたPromiseを返す。
引数がDeferredまたはPromiseの場合、引数のオブジェクトがすべてresolvedになると、返されたPromiseもresolovedになる。引数のオブジェクトのうち1つでもrejectedになると、返されたオブジェクトはrejectedになる。
引数がDeferred、Promiseではない場合、resolvedなものとして扱われる。
when() は、then() による「直列の連結」に対して「並列の連結」と言われる。when() を利用するケースとしては、複数の非同期処理を実行し、すべての非同期処理が終了したところで新たな処理を行うことが考えられる。

$.when(delayHello(), delayHello3000())
.done(hello);

上記のコードでは、delayHello() の返すPromiseと delayHello3000() が返すPromiseの両方がresolovedになると、done() で渡された hello() が実行される。以下のように出力される。

Hello!       ←2秒後
Hello! 3000  ←3秒後
Hello sync   ←3秒後

参考ページ:
1.爆速でわかるjQuery.Deferred超入門 - Yahoo! JAPAN Tech Blog
2.JavaScriptとコールバック地獄 - Yahoo! JAPAN Tech Blog
3.Types | jQuery API Documentation
4.$.when() - jQuery API Documentation 日本語訳
5.jQuery.DeferredとかjQuery.whenの使い方について - 一から勉強させてください( ̄ω ̄;)

« PHPメモ037:cURLでHTTPSなサイトにアクセスする | Main | jQueryのAjax関数はPromiseを実装したjqXHRを返す »

JavaScript」カテゴリの記事

Comments

Post a comment

(Not displayed with comment.)

TrackBack

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

Listed below are links to weblogs that reference Deferredの復習:

« PHPメモ037:cURLでHTTPSなサイトにアクセスする | Main | jQueryのAjax関数はPromiseを実装したjqXHRを返す »

March 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 31  
無料ブログはココログ

日本blog村

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

好きな音楽家

メモ

XI-Prof