Edited at

今さら聞けないjQuery.Deferred入門

More than 3 years have passed since last update.


はじめに


  • 何となくDeferredを知っているがちゃんと使ったことのない人向けの記事です。

  • 本文中の p はPromiseオブジェクトを指します。(追記:Deferredオブジェクトを使うこともありますが、普通はPromiseに対して .then().done() をぶら下げます。)


いきなり具体例

1 が出力された1秒後に 2done! が同時に出力される。


waterfall.js

function test_deferred() {

var p = new $.Deferred().resolve().promise();

p = p
.then(function() {
var d = new $.Deferred();
setTimeout(function() {
console.log(1);
d.resolve();
}, 1000);
return d.promise();
});

p = p
.then(function() {
var d = new $.Deferred();
setTimeout(function() {
console.log(2);
d.resolve();
}, 1000);
return d.promise();
});

return p;
}

test_deferred().done(function(){console.log("done!!")});


並列的に処理を実行させる場合は $.when() を用いる。


parallel.js

// titleとcomment両方が取れるまでPUTを待つ

var title, comment;

var titleDeferred = (function() {
var dInner = new $.Deferred();
self.trigger('getTitle', todo, function(res) {
title = res;
dInner.resolve();
});
return dInner.promise();
})();

var commentDeferred = (function() {
var dInner = new $.Deferred();
self.trigger('getComment', todo, function(res) {
comment = res;
dInner.resolve();
});
return dInner.promise();
})();

$.when(titleDeferred, commentDeferred).done(function() {
// PUT要求を送る(タイトルとコメント両方がとれたタイミングで)
todo.save({ id: todo.get('id'), title: title, comment: comment });
});


(応用)可変数のDeferredを並列実行させたいときは func.apply(ctx, argArr) を用いる。


parallelApply.js

var firstDeferred = (function() {

var dInner = new $.Deferred();
setTimeout(function() {
console.log(1);
dInner.resolve();
}, 1000);
return dInner.promise();
})();

var secondDeferred = (function() {
var dInner = new $.Deferred();
setTimeout(function() {
console.log(2);
dInner.resolve();
}, 1500);
return dInner.promise();
})();

// 配列に並列実行させるDeferredオブジェクト群をぶちこむ。
var deferredArray = [firstDeferred, secondDeferred];

// 可変数のDeferredを並列実行させる
$.when.apply($, deferredArray).done(function() {
console.log('done');
});



.then().done() の使い分け


Case1 : p.then(callbackA).then(callbackB)...


  • この場合は、callbackAで返す Promiseオブジェクトresolve() されるまで、次の.then()ブロックには入らない。


  • p.then(callback).then(callback).then(callback).done(callback) という流れをよく見る。

  • thenでwaterfallにチェインしたい際は 必ず callbackで Promiseオブジェクト をreturnする必要がある。(追記:Deferredオブジェクトを返しても良いが普通はPromiseオブジェクトを返す。)

  • returnしないとparallelにcallbackが実行されてしまう。正確には、ひとつ前のPromiseの状態を引き継ぐ挙動になる。


Case2 : p.done(callbackA).done(callbackB)


  • この場合は、大元の p がresolve()された際に、並列的にcallbackA, callbackBが走る。

  • (当然だがdoneでチェインする際は、callbackで Promiseオブジェクト をreturnする必要なし)


Case3 : p.then(callbackA).done(callbackB).done(callbackC)


  • この場合は、callbackAで返す Promiseオブジェクト がresolve()された『後』にcallbackB、callbackCが並列的に走る。