jQueryのdeferredオブジェクトの詳細(二)

27119 ワード

Deferredオブジェクトは$.Deferred構造、$.Deferredは簡単な工場モードとして実現された.
  $.Deferredの実装
  • は3つの$を作成する.Callbacksオブジェクトは、それぞれ成功done、失敗fail、処理中processの3つの状態
  • を表す.
  • は3つの処理結果に対応し、resolve、reject、notify
  • は、state、always、then、primiseメソッド
  • を有するpromiseオブジェクトを作成する.
  • primiseオブジェクトを拡張することによって最終的なDeferredオブジェクトを生成し、他のコードを阻止してこのdeferredオブジェクトの状態を変更する、deferred.promise()はdeferredオブジェクトの状態を変えることができず、作用も現在の状態が変わらないことを保証するものではない
  • Deferredの3ドルについてお話ししましたCallback()のインスタンスで、Deferred自体はこの3つのオブジェクトを中心により高度な抽象化を行います.以下はDeferredオブジェクトのコアメソッドです
      1.done/fail/progressはcallbacksです.add、コールバック関数をコールバックマネージャに追加
      2.resolve/reject/notifyはcallbackです.Fire、コールバック関数(またはキュー)の実行
      $.Deferredメンバーのメソッド
      1.$.Deferred()
    非同期キューインスタンスオブジェクトdeferredを生成
    functionパラメータを受け入れます.functionではthisを使用して現在の非同期キューインスタンスを呼び出すことができます.
      2.deferred.done(fn)
    成功時にトリガーされるコールバックfn
      3.deferred.fail(fn)
    失敗時にトリガーされるコールバック
      4.deferred.progress(fn)
    処理中にトリガーされたコールバック
      5.deferred.resolve/resolveWith([context],args)
    ここは$に等しい.Callbacks().(fire/fireWith)
    タスク処理に成功した後、このメソッドを使用して成功イベントをトリガーし、doneキューに追加されたコールバックがトリガーされます.
      6.deferred.reject/rejectWith([context],args)
    ここは$に等しい.Callbacks().(fire/fireWith)
    タスク処理に成功した後、このメソッドを使用して成功イベントをトリガーします.failキューに参加したコールバックがトリガーされます.
      7.deferred.notify/notifyWith([context],args)
    ここは$に等しい.Callbacks().(fire/fireWith)
    タスク処理では、このメソッドを使用して処理中のイベントをトリガーできます.
      8.deferred.promise()
      deferred.promise()は、deferredオブジェクトの状態を変更するために他のコードをブロックするだけです.promise()メソッドが返すdeferredオブジェクトは,resolve,reject,progress,resolveWith,rejectWith,progressWithといった状態を変える方法がない.
      9.deferred.then([fnDone,fnFail,fnProgress])
    done|fail|progressの3つの状態のコールバックに対応する3つのコールバック関数を直接入力ことができる.when($.ajax("/main.php")).then(successFunc,failureFunc,proFunc)、1番目のパラメータはdone()メソッドのコールバック関数、2番目のパラメータはfailメソッドのコールバック関数、3番目のパラメータはprogressのコールバック関数、または1つのパラメータのみを伝えることができ、done()メソッドのコールバック関数
      10.deferred.always(fn)
    最後にresolveでもrejectでもfnがトリガーされます
      11.$.when(mission1,[mission2,mission3,...])
    この件をjQueryに載せる方法は、複数の操作にコールバック関数、例$を指定する.when($.ajax("test1.html",$.ajax("test2.html"))).done(fn).fail(fn)
      $.Deferredソース構造
        /**
        Deferred      ,      
        */
        jQuery.extend({
            /**
                Deferred  ,"  "         。     Deferred,      ,         
              :     ,    deferred, deferred      ,  func.call(deferred,deferred)     deferred     func     
              :         (    ),            ,          (API)。
            * @param {Function} func         (  call  , deferred         )
            * @return {Object} deferred             
            */
            Deferred: function (func) {
                /**
                 *          
                 *                 (deferred)     : 
                 * 
                 Deferred                   
                          (          (   ))
                           (      (   ))
                             (jQuery.Callbacks  )
                             (       )
                 *     ,           callbacklist   doneList, failList, processList
                 * resolve         ,       “   ”  (deferred        “   ”  “   ”,  done(   ))
                 * reject          ,       “   ”  ,deferred       , “   ”  “   ”,  fail(   )
                 * notify          ,       “     ,     ”  
                 * done       (    )
                 * fail       (    )
                 * progress    
                 * resolved     
                 * rejected     
                 */
                var tuples = [
     
                        // action     , add listener      (      ),listener list      (        ),final state       
                        ["resolve", "done", jQuery.Callbacks("once memory"), "resolved"],
                        ["reject", "fail", jQuery.Callbacks("once memory"), "rejected"],
                        ["notify", "progress", jQuery.Callbacks("memory")]
                ],
                //        ,(deferred   )    :pending(    ) resolved(    ) rejected(    )  
                    state = "pending",
                    
                    /**
                    *     promise               ,       ,       
                    *   :1.    deferred   ,promise        extend deferred  ,    
                    *       2.  deferred  ,      deferred       ,        ,       deferred  ,   done,fail,progress  
                    */
                    promise = {
                        /**
                               
                        * return {String}        (    )
                        */
                        state: function () {
                            return state;
                        },
                        /**
                             resolve  reject,    fn
                                             
                           doneList failList list       (  ),  deferred             ,          
                        * return {Object}                          
                        */
                        always: function () {
                            deferred.done(arguments).fail(arguments);
                            return this;
                        },
                        /**
                              ,  3        ,       ,    deferred  
                        * @param {Function} fnDone       ,done()       
                        * @param {Function} fnFail       ,fail()       
                        * @param {Function} fnProgress        ,progress()          
                        * @return {Object}             
                        */
                        then: function ( /* fnDone, fnFail, fnProgress */) {
                            //       ,         funs
                            var fns = arguments;
                            // newDefer          
                            return jQuery.Deferred(function (newDefer) {
                                jQuery.each(tuples, function (i, tuple) {
                                    // i       tuples   
                                    // tuple   tuples[i]                                  
                                    var action = tuple[0],//         tuple[0]        resolve,reject,notify
                                        fn = jQuery.isFunction(fns[i]) && fns[i];//      
    
                                    /**
                                                                                      
                                    * deferred       
                                    * tuple[1] [done | fail | progress]
                                    * fn          
                                    *    deferred   callbacklist(  )      ,  fn      ,      :
                                    * 1. fn    ,(  :undefined null),     newDefer   ([resolve | reject | notify]  ),         newDefer          (     )deferred      (done,fail,progress)
                                    * 2. fn   ,     (ret)      deferred  ,      :
                                    *    a)     deferred  ,       newDefer   ([resolve | reject | notify]  )   ret         ,   newDefer     ret   
                                    *    b)      deferred  ,   ret  newDefer   ,  this        deferred,   newDefer       ,   this         ,             ,Ps:  newDefer   (  )        deferred   (   )。
                                    */
                                    //   deferred.done,deferred.fail,deferred.progess    ;
                                    deferred[tuple[1]](function () {
                                        //             (    )
                                        var returned = fn && fn.apply(this, arguments);
                                        if (returned && jQuery.isFunction(returned.promise)) {
                                            returned.promise()
                                                .done(newDefer.resolve)
                                                .fail(newDefer.reject)
                                                .progress(newDefer.notify);
                                        } else {
                                            newDefer[action + "With"](this === promise ? newDefer.promise() : this, fn ? [returned] : arguments);
                                        }
                                    });
                                });
                                //
                                fns = null;
                            }).promise();//            
                        },
    
                        /**
                                  ( deferred    promise  )       
                        * @param  {Object}        
                        * @return {Object}        , promise        ,     promise  
                        */
                        promise: function (obj) {
                            return obj != null ? jQuery.extend(obj, promise) : promise;
                        }
                    },
                    //             
                    deferred = {};
     
                //        pipe then  ,    pipe  filter  
                promise.pipe = promise.then;
     
                
                jQuery.each(tuples, function (i, tuple) {
                    //        ,         list,3 $.Callback()    
                    var list = tuple[2],
                        stateString = tuple[3]; //         resolved,rejected
     
                    //           (    )
                    // promise[done|fail|progress]=list.add;
                    //                     add  ,            list      [done|fail|progress],    
                    promise[tuple[1]] = list.add;
     
                    /*
                         
                    * add          
                    * fire   fireWidth
                    * fireWidth         
                    */
                    //   stateString    ,progress   ,     [done|fail]            
                    //         :1.        ;2.          ;3.  (  )          
                    if (stateString) {
                        list.add(function () {
                            // state = [ resolved | rejected ]
                            //           
                            state = stateString;
                             //         
                            //   0^1 = 1   1^1 = 0
                            ////// [ reject_list | resolve_list ].disable; progress_list.lock
                        }, tuples[i ^ 1][2].disable, tuples[2][2].lock);
                    }
                    //                     (     )     
                    // deferred[ resolve | reject | notify ]
                    deferred[tuple[0]] = function () {
                        deferred[tuple[0] + "With"](this === deferred ? promise : this, arguments);
                        return this;
                    };
                    deferred[tuple[0] + "With"] = list.fireWith;
                });
     
    
                //                   ( promise  extend deferred )
                promise.promise(deferred);
     
    
                //   func    ,                      ,   this       
                if (func) {
                    func.call(deferred, deferred);
                }
     
    
                //        
                return deferred;
            },
     
            //   $.when     
            //          ,        。
            //   Deferred  ,      
            //     function ,      
            when: function (subordinate /* , ..., subordinateN */) {
                var i = 0,
                    //arguments     
                    resolveValues = core_slice.call(arguments),
                    length = resolveValues.length,
                    
                    //          
                    //subordinate && jQuery.isFunction(subordinate.promise)  subordinate   Deferred     
                    remaining = length !== 1 || (subordinate && jQuery.isFunction(subordinate.promise)) ? length : 0,
     
                     //           
                    deferred = remaining === 1 ? subordinate : jQuery.Deferred(),
                    
                    //       |        
                    ////          ,        。
                    updateFunc = function (i, contexts, values) {
                        return function (value) {
                            contexts[i] = this;
                            values[i] = arguments.length > 1 ? core_slice.call(arguments) : value;
                            
                            //
                            if (values === progressValues) {
                                deferred.notifyWith(contexts, values);
                            } else if (!(--remaining)) {
                              //  ,            0 
                              ////                                                 
                                deferred.resolveWith(contexts, values);
                            }
                        };
                    },
     
                    progressValues, progressContexts, resolveContexts;
     
                 ////    1             
                if (length > 1) {
                    progressValues = new Array(length);
                    progressContexts = new Array(length);
                    //
                    resolveContexts = new Array(length);
                    for (; i < length; i++) {
                        if (resolveValues[i] && jQuery.isFunction(resolveValues[i].promise)) {
                         //       
                            resolveValues[i].promise()
                                 //              
                                .done(updateFunc(i, resolveContexts, resolveValues))
                                 //          ,        。          
                                .fail(deferred.reject)
                                //                  
                                .progress(updateFunc(i, progressContexts, progressValues));
                        } else {
                            //       , remain       
                            --remaining;
                        }
                    }
                }
     
                //            
                if (!remaining) {
                    deferred.resolveWith(resolveContexts, resolveValues);
                }
     
                return deferred.promise();
            }
        });