はじめに
- 何となくDeferredを知っているがちゃんと使ったことのない人向けの記事です。
- 本文中の
p
はPromiseオブジェクトを指します。(追記:Deferredオブジェクトを使うこともありますが、普通はPromiseに対して.then()
や.done()
をぶら下げます。)
いきなり具体例
1 が出力された1秒後に 2 と done! が同時に出力される。
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が並列的に走る。