9
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

jQuery Deferred の実装を調べてみた

Last updated at Posted at 2012-12-26

$.Deferred

コンストラクタである $.Deferred() 内では、まず Promise が作られ、それをクローンして resolve(), reject(), progress() を足した Deferred が返される。

以下は https://github.com/jquery/jquery/blob/1.7.2/src/deferred.js から抜粋。

jQuery.extend({
  Deferred: function( func ) {
    var doneList = jQuery.Callbacks( "once memory" ),
        failList = jQuery.Callbacks( "once memory" ),
        progressList = jQuery.Callbacks( "memory" ),
        state = "pending",
        lists = {
          resolve: doneList,
          reject: failList,
          notify: progressList
        },
        promise = {
          done: doneList.add,
          fail: failList.add,
          progress: progressList.add,
          // 中略...
        },
        deferred = promise.promise({}),
        key;

    for ( key in lists ) {
      deferred[ key ] = lists[ key ].fire;
      deferred[ key + "With" ] = lists[ key ].fireWith;
    }

    // 中略...

    return deferred;
  }
  // 後略...
});

この PromiseDeferred はクロージャ内の同じ変数を操作する。つまり、インターフェイスは異なるが、持っている 状態 は同じ。ここでいう 状態 とは、基本的には resolve(), reject(), progress() に対応した doneList, failList, progressList という三つの $.Callbacks のこと。

$.Callbacks

ここで出てきた Callbacks とは、jQuery 1.7 から導入された、複数のコールバックを管理してくれるオブジェクト。ざっくり言うと add() でコールバック関数を追加し、fire() で実行する。ちなみに Callbacks のメソッドは this を返すので、メソッドチェーンできる。

function echo(message) {
  console.log(message);
}
$.Callbacks()
  .add(echo)
  .fire('hello');
// => hello と表示される。

フラグ

コンストラクタである $.Callbacks() には、挙動を変えるためのフラグを引数として渡すことができる。複数のフラグが指定可能。$.Deferred の実装で使われている oncememory だけ見てみる。

once フラグ

一回しか fire() できない Callbacks になる。

$.Callbacks('once')
  .add(echo)
  .fire('hello')
  .fire('world');
// => hello しか表示されない。

memory フラグ

最後の fire() の実行を覚えていて、その後に add() しても、コールバックが実行される。なお、事前に複数回 fire() されていても、コールバックは一回しか実行されない。引数は最後に実行された fire() のものが使われる。

$.Callbacks('memory')
  .fire('hello')
  .fire('world')
  .add(echo);
// => world とだけ表示される。

disable() と lock() の違い

disable()lock() は、$.Deferred の実装の中で使われており、Callbacks を無効にするらしいが、ドキュメントを読んでもいまいち違いがよくわからなかった。ソースを見ると memory フラグがある時の挙動が違う様子。disable() するとコールバックがいっさい実行されなくなるのに対して、lock() の後に add() した場合はコールバックが実行される。

$.Callbacks('memory')
  .fire('hello')
  .disable()
  .add(echo);
// => 何も表示されない。
$.Callbacks('memory')
  .fire('hello')
  .lock()
  .add(echo);
// => hello と表示される。

$.Deferred での $.Callbacks の使われ方

resolve(), reject(), progress() の主な働きは、対応する Callbacksfire() すること。

Deferred は一度しか resolve()/reject() できないようになっている。doneList, failListonce 指定されているので一回しか fire() できない。さらに resolve()/reject() は、他方の $.Callbacks を disable() し、progressListlock() する。

ただし、三つの Callbacks は memory フラグが指定されているので、fire() された後も add() されればコールバックが実行される。このため resolve() した後でも done() などで足したコールバック関数は即座に実行される。

9
8
0

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
9
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?