深刻理解jQuery中的Deferred

引入

  1  在開發的過程當中,咱們常常遇到某些耗時很長的javascript操做,而且伴隨着大量的異步。javascript

  2  好比咱們有一個ajax的操做,這個ajax從發出請求到接收響應須要5秒,在這5秒內咱們能夠運行其餘代碼段,當響應到達後,咱們須要判斷響應的結果(無非就是成功或者失敗),並根據不一樣的結果  添加回調函數php

  3  爲了有效的簡潔的添加回調函數jQuery引入了Callbacks。html

  4  而爲了方便的 根據不一樣的結果(或者根據各類跟結果有關的邏輯,好比不論是成功或者失敗) 添加回調函數,jQuery引入了Deferred。java

 $.ajax("test.html")
  .done(function(){ alert("success"); })
  .fail(function(){ alert("error"); });

  5  於是Deferred與Callbacks是密不可分的,事實上,Callbacks也是從Deferred中分離出去的ajax

回顧Callbacks

  1  Callbacks大致架構

  2 Callbacks源碼分析:

  源碼講解

  另外要注意下面兩個參數:數組

    once:若是建立Callbacks時加入該參數,則運行數組中的全部回調函數以後,也就是fire()以後,會清空數組。promise

    memory:會保存上一次運行fire(args)時的參數args,每當添加一個新的回調函數到數組中,會當即使用args做爲參數調用新加的函數一次架構

 

Deferred講解:

Deferred大致架構:

 

1  先來看一看tuple數組app

複製代碼
var tuples = [
                // action, add listener, listener list, final state
                [ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ],// 解決 操做成功  Callbacks對象 最終狀態爲解決
                [ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ], // 拒絕  操做失敗 Callbacks對象 最終狀態爲拒絕
                [ "notify", "progress", jQuery.Callbacks("memory") ]  // 通知  操做進行中 Callbacks對象 最終狀態無(操做進行中的最終狀態就是操做完成,完成無非就是轉變爲上面兩種 成功或者失敗)
            ]
複製代碼

 jQuery的設計理念是這樣的:異步

  1  deferred對象有三種執行狀態----完成 失敗 進行中。

  2  每種狀態對應一個Callbacks實例

  3  若是執行狀態是"完成"(resolved),deferred對象馬上調用done()方法指定的回調函數(也就是執行已完成狀態對應的Callbacks實例的fire方法);若是執行狀態是"失敗",調用fail()方法指定的回調函數;若是執行狀態是"進行中",則繼續等待,或者調用progress()方法指定的回調函數。

 

2  promise對象和deferred對象

一個是deferred外部接口對象,一個是內部promise對象。

promise對象是一個受限的對象, 這就是所謂的受限制的deferred對象,由於相比deferred對象, promise對象沒有resolve(With), reject(With), notify(With)這些能改變deferred對象狀態而且執行callbacklist的方法了,只能是then、done、fali等方法。

 

3  done  fail  progress 方法 與 resolve  reject  notify方法

  在上圖中咱們已經說明了前三個方法就是Callbacks中的add方法。後三個調用了Callbacks中的fireWith()方法。

  因此,咱們能夠總結出:

    1  三種狀態各對應一個Callbacks實例

    2  使用done 或fail 或progress時,實際上就是往各自對應的Callbacks實例中的list數組添加回調函數

    3  使用resolve 或reject 或notify時,則就是運行各自對應Callbacks實例中的list數組中的回調函數

 

4  then方法

  then方法建立了一個新的promise對象,then就是pipe,咱們能夠想象是一個管道。管道就是能 ‘承上啓下’(更貼切的來講,在Deferred中的then只作了承上,僅僅是我的觀點)

複製代碼
var a = $.Deferred();
a.then(function(val){
    console.log(val); // 2
    return val * 2
}).then(function(val){
    console.log(val); // 4
});
a.resolve(2)
複製代碼

如案例所示,下一個回調對象都能取到上一個回調對象的值,這樣一直能夠疊加日後傳遞。

關於then可能看了很是迷糊,沒關係,上面說的是then的高級特性,平時咱們基本不怎麼使用的。

平時咱們大部分是使用then來代替done以及fail:

$.when($.ajax( "test.php" ))
  .then(successFunction, failureFunction );

 

deferred源碼分析

複製代碼
    define([
    "./core",
    "./var/slice",
    "./callbacks"
], function( jQuery, slice ) {

jQuery.extend({

    Deferred: function( func ) {
        // 建立一個tuples數組,一個promise對象,一個deferred對象,一個state變量
        var tuples = [
                // action, add listener, listener list, final state
                [ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ],// 解決 操做成功  Callbacks對象 最終狀態爲解決
                [ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ], // 拒絕  操做失敗 Callbacks對象 最終狀態爲拒絕
                [ "notify", "progress", jQuery.Callbacks("memory") ]  // 通知  操做進行中 Callbacks對象 最終狀態無(操做進行中的最終狀態就是操做完成,完成無非就是轉變爲上面兩種 成功或者失敗)
            ],
            state = "pending",
            promise = {
                state: function() {  // 返回當前狀態
                    return state;
                },
                always: function() {
                    deferred.done( arguments ).fail( arguments );
                    return this;
                },
                then: function( /* fnDone, fnFail, fnProgress */ ) {
                    var fns = arguments;
                    return jQuery.Deferred(function( newDefer ) {
                        jQuery.each( tuples, function( i, tuple ) {
                            var fn = jQuery.isFunction( fns[ i ] ) && fns[ i ];
                            // deferred[ done | fail | progress ] for forwarding actions to newDefer
                            deferred[ tuple[1] ](function() {  // 注意,這裏的deferred指的不是新Deferred對象中的deferred(也就是否是指的newDefer)
                                var returned = fn && fn.apply( this, arguments );
                                // 若returned是一個deferred對象,則爲returned添加一個回調函數,這個回調函數運行後使newDefer可以接收到returned的fire()參數
                                if ( returned && jQuery.isFunction( returned.promise ) ) {
                                    returned.promise()
                                        .done( newDefer.resolve )
                                        .fail( newDefer.reject )
                                        .progress( newDefer.notify );
                                } else {
                                    newDefer[ tuple[ 0 ] + "With" ](
                                        this === promise ? newDefer.promise() : this,
                                        fn ? [ returned ] : arguments
                                    );
                                }
                            });
                        });
                        fns = null;
                    }).promise();
                },
                // Get a promise for this deferred
                // If obj is provided, the promise aspect is added to the object
                promise: function( obj ) { // 若obj非null,則將obj與promise對象結合並返回,不然返回promise對象
                    return obj != null ? jQuery.extend( obj, promise ) : promise;
                }
            },
            deferred = {};

        // Keep pipe for back-compat  和以前的版本兼容
        promise.pipe = promise.then;

        // Add list-specific methods
        jQuery.each( tuples, function( i, tuple ) {
            var list = tuple[ 2 ], // 這是一個Callbacks 對象
                stateString = tuple[ 3 ]; // 用字符串表示的狀態

            // promise[ done | fail | progress ] = list.add
            promise[ tuple[1] ] = list.add;  // Callbacks對象的add函數

            // Handle state  處理狀態
            if ( stateString ) {
                list.add(function() {
                    // state = [ resolved | rejected ]
                    state = stateString;  // Callbacks對象中的回調函數列表第一項:改變狀態

                // [ reject_list | resolve_list ].disable; progress_list.lock
                },
                tuples[ i ^ 1 ][ 2 ].disable,   //  由於reject,resolve是對立的,當行爲爲reject,那麼resolve的Callbacks就無用了,將其回調函數列表清空便可
                tuples[ 2 ][ 2 ].lock ); // 當行爲爲reject或者resolve時,即"結果已肯定",那麼就不容許再調用 "操做進行中" 的Callbacks對象的fire()了
            }

            // 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;
        });

        // Make the deferred a promise 將promise合併至deferred
        promise.promise( deferred );

        // Call given func if any  在初始化完deferred對象後,會當即運行func函數,並把deferred做爲第一個參數傳入
        if ( func ) {
            func.call( deferred, deferred );
        }

        // All done!
        return deferred;
    },

    // Deferred helper
    when: function( subordinate /* , ..., subordinateN */ ) {
        /* 大致思路:
         *    若參數(參數必需是deferred對象)只有一個,則返回這個參數的promise對象
          *   若參數有多個,則生成一個新的deferred對象,並返回deferred對象的promise對象
          *      當全部參數的狀態爲完成時,使新deferred對象的狀態變爲完成,
          *      如有一個參數的狀態爲失敗,則使新deferred對象的狀態變爲失敗
          *
          * */
        var i = 0,
            resolveValues = slice.call( arguments ),//slice是數組的slice方法,通常狀況下:resolveValues就是一個由deferred組成的數組
            length = resolveValues.length,

            // the count of uncompleted subordinates 沒有運行完成的deferred對象的數量
            // 若是length長度不爲1或者subordinate是一個deferred對象則,remaining=length,不然remaining爲0
            remaining = length !== 1 ||
                ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0,//運算符優先級: 邏輯與邏輯或 優先級高於 ? :

            // the master Deferred.
            // If resolveValues consist of only a single Deferred, just use that.
            // 若是remaining爲1,則使用subordinate(這是一個deferred對象)便可,不然建立一個新的deferred對象
            deferred = remaining === 1 ? subordinate : jQuery.Deferred(),

            // Update function for both resolve and progress values
            updateFunc = function( i, contexts, values ) {
                return function( value ) {
                    contexts[ i ] = this;
                    values[ i ] = arguments.length > 1 ? slice.call( arguments ) : value;
                    if ( values === progressValues ) {
                        deferred.notifyWith( contexts, values );
                    } else if ( !( --remaining ) ) {
                        deferred.resolveWith( contexts, values );
                    }
                };
            },

            progressValues, progressContexts, resolveContexts;

        // Add listeners to Deferred subordinates; treat others as resolved
        if ( length > 1 ) {
            progressValues = new Array( length );
            progressContexts = new Array( length );
            resolveContexts = new Array( length );
            // 迭代resolveValues中的每個deferred對象,爲其添加不一樣狀態下的回調函數
            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 {
                    --remaining;
                }
            }
        }

        // If we're not waiting on anything, resolve the master
        if ( !remaining ) {
            deferred.resolveWith( resolveContexts, resolveValues );
        }

        return deferred.promise();
    }
});

return jQuery;
});
複製代碼
相關文章
相關標籤/搜索