jQuery 1.5のDeferredを翻訳してみた

Deferred Object

バージョン1.5で導入された jQuery.Deferred は、コールバックキューに複数のコールバックメソッドを登録し、コールバックキューから登録されたコールバックメソッドを呼び出し、同期的/非同期的な関数のいずれかの success または failure 状態を中継する機能を可能にする連鎖的なユーティリティオブジェクトです。

jQuery.Deferred, introduced in version 1.5, is a chainable utility object that can register multiple callbacks into callback queues, invoke callback queues, and relay the success or failure state of any synchronous or asynchronous function.

Javascriptでは、普通、追加的なコールバックを受ける関数を呼び出す。そしてその関数の中でコールバックが呼び出される。
In Javascript it is common to invoke functions that optionally accept callbacks that are called within that function.

例えば、 jQuery 1.5 より前のバージョンでは、 jQuery.ajax() のような非同期処理は、コールバック関数を取り、このコールバック関数は、近い将来 ajax リクエストの success 、 error 、 completion が生じたときに呼び出される。

For example, in versions prior to jQuery 1.5, asynchronous processes such as jQuery.ajax() accept callbacks to be invoked some time in the near-future upon success, error, and completion of the ajax request.

jQuery.Deferred は、コールバック関数の管理、呼び出しの方法を強化したものです。
jQuery.Deferred は、複数のコールバック関数を追加する方法を提供し、これらのコールバック関数はオリジナルのコールバック登録がすでに済んでいるかどうかには関わらず呼び出される。
jQuery Deferred は CommonJS Promises/A に基づいて設計されている。

Query.Deferred introduces several enhancements to the way callbacks are managed and invoked. In particular, jQuery.Deferred provides flexible ways to provide multiple callbacks, and these callbacks can be invoked regardless of whether the original callback dispatch has already occurred. jQuery Deferred is based on the CommonJS Promises/A design.

既知の Deferred のモデルは、 chain-aware 関数ラッパーとして考えられている。
deferred.then(), deferred.done(), そして deferred.fail() メソッドは呼び出される関数を指定し、 deferred.resolve(args) または deferred.reject(args) メソッドは、与えた引数でメソッドを呼び出す。
一度、Deferred が resolve または reject されると、次の状態になる。
例えば、2番目の deferred.resolve() 呼び出しは無視される。
もし、 Deferred が resolve された後に、deferred.then() により他の関数が追加されていれば、これらの関数は、先に提供された引数で即座に呼び出される。

One model for understanding Deferred is to think of it as a chain-aware function wrapper. The deferred.then(), deferred.done(), and deferred.fail() methods specify the functions to be called and the deferred.resolve(args) or deferred.reject(args) methods “call” the functions with the arguments you supply. Once the Deferred has been resolved or rejected it stays in that state; a second call to deferred.resolve() is ignored for example. If more functions are added by deferred.then() after the Deferred is resolved, they are called immediately with the arguments previously provided.

jQuery API 呼び出しで、 Deferred または jQuery.ajax() や jQuery.when() のようなDeferred-compatible なオブジェクトが返されるようなケースでは、Deferred キューにコールバックを追加するために、 deferred.then() 、 deferred.done() 、 deferred.fail() メソッドを利用することを勧めます。
API コールまたは Deferred を生成したコードの内部では、適切なコールバック起動を呼び出すポイントで、 deferred から deferred.resolve() または deferred.reject() が呼び出される。

In most cases where a jQuery API call returns a Deferred or Deferred-compatible object, such as jQuery.ajax() or jQuery.when(), you will only want to use the deferred.then(), deferred.done(), and deferred.fail() methods to add callbacks to the Deferred’s queues. The internals of the API call or code that created the Deferred will invoke deferred.resolve() or deferred.reject() on the deferred at some point, causing the appropriate callbacks to run.

jQuery.Deferred Constructor

jQuery.Deferred コンストラクタは新しい Deferred オブジェクトを生成する。新しいオペレータはオプションです。

The jQuery.Deferred constructor creates a new Deferred object. The new operator is optional.

jQuery.Deferred は付与的な関数を渡される。その関数はコンストラクタが返却される前に呼びだされ、生成された deferred オブジェクトに最初のオブジェクトとして、または、関数の第一引数としての両方の方法で渡される。
呼び出される関数は、例えば deferred.then() を落ちいてコールバックを追加する。

jQuery.Deferred can be passed an optional function, which is called just before the constructor returns and is passed the constructed deferred object as both the this object and as the first argument to the function. The called function can attach callbacks using deferred.then() for example.

Deferred オブジェクトは、unresolved な状態からスタートする。 deferred.then() 、 deferred.done() および deferred.fail() を伴うオブジェクトが付与されたあらゆるコールバックは、後に実行されるキューに入る。 deferred.resolve() または deferred.resolveWith() の呼び出しで、 Deferred は resolved な状態に入り、セットされたあらゆる doneCallback を即座に実行する。
deferred.reject() または deferred.rejectWith() の呼び出しで、 Deferred は rejected 状態に入り、即座じセットされた failCallbackを実行する。
一度、 オブジェクトが、resolved または rejected な状態に入ると、その状態を維持する。
コールバックは、 resolved または rejected な Deferred に追加する。これらのコールバックは即座に実行する。

A Deferred object starts in the unresolved state. Any callbacks added to the object with deferred.then(), deferred.done(), or deferred.fail() are queued to be executed later. Calling deferred.resolve() or deferred.resolveWith() transitions the Deferred into the resolved state and immediately executes any doneCallbacks that are set. Calling deferred.reject() or deferred.rejectWith() transitions the Deferred into the rejected state and immediately executes any failCallbacks that are set. Once the object has entered the resolved or rejected state, it stays in that state. Callbacks can still added to the resolved or rejected Deferred ― they will execute immediately.

Deferred オブジェクトは連鎖的で、 jQuery オブジェクトが連鎖的なことと似ている。しかし、それはそれ自身のメソッドではない。
Deferred オブジェクトを生成した後で、オブジェクト生成からの直接的な連鎖、または、オブジェクトを変数として保存し1つ以上のメソッドをその変数を付与することのどちらかによって先に上げたメソッドを利用することができます。

The Deferred object is chainable, similar to the way a jQuery object is chainable, but it has its own methods. After creating a Deferred object, you can use any of the methods below by either chaining directly from the object creation or saving the object in a variable and invoking one or more methods on that variable.

結局

  • whenとかajaxがDeferredオブジェクトを返します
$.when($.ajax('./hoge.html'), $.ajax('./fuga.php')).
  done(function(hoge, fuga){ // 成功時= resolve
  }).
  fail(function(){ // 失敗時= reject
  });
  • then は 2つのコールバック(success, failure) を指定します
    • doneだとsuccessのみ、failだとfailureのみです
    • successはresolve状態(deferredがすべて正常に解決した場合)になった場合に呼ばれます
    • failureはreject状態(いずれかのdeferredがこけた場合)になった場合に呼ばれます
  • resolve(arg)、reject(arg)は強制的にdone、failを呼び出します。
    • 例えば、JSON-RPCでやり取りしていて「HTTPとしては200で正常にデータは返ってきたけど、内部のerrorがnullじゃなかった場合、とかの制御ができます
  • 僕の知識が足りてないので不安はありますが、これ、then系の引数に関数だけじゃなくてdeferredを取らないとあんまり面白くない・・・
    • つまり下みたいに、deferredの結果次第で次に流したりしたいじゃないですか
$.when($.ajax('./hoge.json')).
  then($.ajax('./hoge-success.json'), 
          $.ajax('./hoge-failure.json')).
  done(function(arg){
     alert('all green');
  }).
  fail(function(arg){
     alert('hoge or hoge-failure failed');
  });
    • もしくはdeferredプロセスの返り値を見て次のdeferredに繋ぎたいじゃないですか
$.when($.ajax('./some.json')).
  done(function(arg){
    if(arg.error){
      return this.reject(arg.error);
    }
    return this.resolve(arg.result);
  }).then(function(arg){
    alert('some.json has no err');
  }, function(err){
    alert('some.json has err');
  });
    • まあ、パラレルで投げられるのでいいんですけど、結局、このままだと入れ子地獄は続くんだなぁ・・・
$.when($.get('./some.json')).
  then(function(arg){
    $.when($.get(arg.result)).
       then(function(arg2){
         $.when($.get(arg2.result)).then....
       }, function(arg2){
           alert('fail 2');
       });
  }, function(arg){
    alert('fail');
  })
  • 結局id:cho45さんのjsDeferred使えば?ってなったり、@snamuraさんのgitが公開されてmongoのdeferredが使いやすくなるのをまてば?って事なんだろうねぇ・・・
    • 結論としては、使いどころ次第、って感じですかね。。。 callback地獄からは開放されるけど、僕らには新しくwhen-then地獄が待っている!!!ってことみたいです。