LoginSignup
80

More than 5 years have passed since last update.

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

Last updated at Posted at 2014-11-17

はじめに

  • 何となく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が並列的に走る。

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
80