首頁  >  文章  >  web前端  >  jQuery原始碼解析Deferred非同步物件的方法

jQuery原始碼解析Deferred非同步物件的方法

一个新手
一个新手原創
2017-10-26 10:21:171366瀏覽

在工作中我們可能會把jQuery選擇做自己專案的基礎函式庫,因為其提供了簡單的DOM選擇器以及封裝了很多實用的方法,例如$.ajax(),它使得我們不用操作xhrxdr對象,直接書寫我們的程式碼邏輯即可。更豐富的是它在ES6沒有原生支援的那段時間,提供了Deferred對象,類似於Promise對象,支援done/ fail/progress/always方法和when批次方法,這可能在專案上幫助過你。

ES6提供了Promise對象,但由於它是內建C++實現的,所以你也沒辦法看它的設計。不如我們透過jQuery的原始碼來探究其設計思路,​​並比較一下兩者的差異。本文採用jquey-3.1.2.js版本,其中英文註解為原版,中文註解為我新增。

jQueryajax整體設計

#jQuery在內部設定了全域的ajax參數,在每一個ajax請求初始化時,用傳遞的參數與預設的全域參數進行混合,並建構一個jqXHR物件(提供比原生XHR更為豐富的方法,同時實現其原生方法),透過傳遞的參數,來判斷其是否跨域、傳遞的參數類型等,設定好相關頭部資訊。同時其被初始化為一個內建Deferred物件用於非同步操作(後面講到),並添加done/fail方法作為回調。同時我們也封裝了$.get/$.post方法來快速呼叫$.ajax方法。

上面提到的Deferred對象,與ES6的Promise物件類似,用於更為方便的非同步操作,多種回呼以及更好的書寫方式。提供progress/fail/done方法,並分別用該物件的notify/reject/resolve方法觸發,可以使用then方法快速設定三個方法,使用always新增都會執行的回調,並且提供when方法支援多個非同步操作合併回調。可以追加不同的回呼列表,其回呼列表是使用內部Callbacks對象,更方便的按照隊列的方式來執行。

Callbacks回呼佇列對象,用於建立易於操作的回呼函數集合,在操作完成後進行執行。支援四種初始化的方式once/unique/memory/stopOnFalse,分別代表只執行依序、去重、快取結果、鍊式呼叫支援終止。提供fired/locked/disabled狀態值,代表是否執行過、鎖定、停用。提供add/remove/empty/fire/lock/disable方法操作回呼函數佇列。

主要涉及的概念就是這三個,不再做延伸,三個物件的設計程式碼行數在1200行左右,斷斷續續看了我一週(´ཀ`」 ∠) 。我們從這三個倒序開始入手剖析其設計。

jQuery.Callbacks物件

Callbacks對象,用於管理回呼函數的多用途清單。它提供了六個主要方法:

  1. add: 在清單中新增回呼函數

  2. remove : 移除清單中的回呼函數

  3. empty: 清空清單中的回呼函數

  1. #fire
  2. : 依序執行清單中的回呼函數

  3. lock
  4. : 對清單上鎖,禁止一切操作,清除數據,但保留快取的環境變數(只在

    memory參數時有用)

  5. #disable: 停用該回呼列表,所有資料都清空

    #在初始化時,支援四個參數,用空格分割:

#once: 此回呼清單只執行依序

memory: 快取執行環境,在新增回呼時執行先執行一次

unique: 去重,每一個函數皆不同(指的是引用位址)stopOnFalse

: 在呼叫中,如果前一個函數回傳
    false
  1. ,中斷清單的後續執行

    我們來看下其實例使用:###
    let cl = $.Callbacks('once memory unique stopOnFalse');
    fn1 = function (data) {
        console.log(data);
    };
    fn2 = function (data) {
        console.log('fn2 say:', data);
        return false;
    };
    cl.add(fn1);
    cl.fire('Nicholas');    // Nicholas
    // 由于我们使用memory参数,保存了执行环境,在添加新的函数时自动执行一次
    cl.add(fn2);    // fn2 say: Nicholas
    // 由于我们使用once参数,所以只能执行(fire)一次,此处无任何输出
    cl.fire('Lee');
    
    // 后面我们假设这里没有传入once参数,每次fire都可以执行
    
    cl.fire('Lee');    // Lee    fn2 say: Lee
    // 清空列表
    cl.empty();
    cl.add(fn2, fn1);
    // 由于我们设置了stopOnFalse,而fn2返回了false,则后添加的fn1不会执行
    cl.fire('Nicholas');    // fn2 say: Nicholas
    // 上锁cl,禁用其操作,清除数据,但是我们添加了memory参数,它依然会对后续添加的执行一次
    cl.lock();
    // 无响应
    cl.fire();
    cl.add(fn2);    // fn2 say: Nicholas
    // 禁用cl,禁止一切操作,清除数据
    cl.disable();
    ###除了上面所說的主要功能,還提供###has/locked/disabled /fireWith/fired###等輔助函數。 ######其所有原始碼實作及註解為:###
    jQuery.Callbacks = function( options ) {
        options = typeof options === "string" ?
            // 将字符串中空格分割的子串,转换为值全为true的对象属性
            createOptions( options ) :
            jQuery.extend( {}, options );
    
        var // Flag to know if list is currently firing
            firing,
    
            // Last fire value for non-forgettable lists
            memory,
    
            // Flag to know if list was already fired
            fired,
    
            // Flag to prevent firing
            locked,
    
            // Actual callback list
            list = [],
    
            // Queue of execution data for repeatable lists
            queue = [],
    
            // Index of currently firing callback (modified by add/remove as needed)
            firingIndex = -1,
    
            // Fire callbacks
            fire = function() {
    
                // Enforce single-firing
                locked = locked || options.once;
    
                // Execute callbacks for all pending executions,
                // respecting firingIndex overrides and runtime changes
                fired = firing = true;
                // 为quene队列中不同的[context, args]执行list回调列表,执行过程中会判断stopOnFalse中间中断
                for ( ; queue.length; firingIndex = -1 ) {
                    memory = queue.shift();
                    while ( ++firingIndex < list.length ) {
    
                        // Run callback and check for early termination
                        if ( list[ firingIndex ].apply( memory[ 0 ], memory[ 1 ] ) === false &&
                            options.stopOnFalse ) {
    
                            // Jump to end and forget the data so .add doesn&#39;t re-fire
                            firingIndex = list.length;
                            memory = false;
                        }
                    }
                }
    
                // Forget the data if we&#39;re done with it
                if ( !options.memory ) {
                    memory = false;
                }
    
                firing = false;
    
                // Clean up if we&#39;re done firing for good
                // 如果不再执行了,就将保存回调的list清空,对内存更好
                if ( locked ) {
    
                    // Keep an empty list if we have data for future add calls
                    if ( memory ) {
                        list = [];
    
                    // Otherwise, this object is spent
                    } else {
                        list = "";
                    }
                }
            },
    
            // Actual Callbacks object
            self = {
    
                // Add a callback or a collection of callbacks to the list
                add: function() {
                    if ( list ) {
    
                        // If we have memory from a past run, we should fire after adding
                        // 如果我们选择缓存执行环境,会在新添加回调时执行一次保存的环境
                        if ( memory && !firing ) {
                            firingIndex = list.length - 1;
                            queue.push( memory );
                        }
    
                        ( function add( args ) {
                            jQuery.each( args, function( _, arg ) {
                                // 如果是函数,则判断是否去重,如果为类数组,则递归执行该内部函数
                                if ( jQuery.isFunction( arg ) ) {
                                    if ( !options.unique || !self.has( arg ) ) {
                                        list.push( arg );
                                    }
                                } else if ( arg && arg.length && jQuery.type( arg ) !== "string" ) {
    
                                    // Inspect recursively
                                    add( arg );
                                }
                            } );
                        } )( arguments );
    
                        if ( memory && !firing ) {
                            fire();
                        }
                    }
                    return this;
                },
    
                // Remove a callback from the list
                // 移除所有的相同回调,并同步将firingIndex-1
                remove: function() {
                    jQuery.each( arguments, function( _, arg ) {
                        var index;
                        while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) {
                            list.splice( index, 1 );
    
                            // Handle firing indexes
                            if ( index <= firingIndex ) {
                                firingIndex--;
                            }
                        }
                    } );
                    return this;
                },
    
                // Check if a given callback is in the list.
                // If no argument is given, return whether or not list has callbacks attached.
                // 检查是否存在该函数,如果不传递参数,则返回是否有回调函数
                has: function( fn ) {
                    return fn ?
                        jQuery.inArray( fn, list ) > -1 :
                        list.length > 0;
                },
    
                // Remove all callbacks from the list
                empty: function() {
                    if ( list ) {
                        list = [];
                    }
                    return this;
                },
    
                // Disable .fire and .add
                // Abort any current/pending executions
                // Clear all callbacks and values
                // 置locked为[],即!![] === true,同时将队列和列表都清空,即禁用了该回调集合
                disable: function() {
                    locked = queue = [];
                    list = memory = "";
                    return this;
                },
                disabled: function() {
                    return !list;
                },
    
                // Disable .fire
                // Also disable .add unless we have memory (since it would have no effect)
                // Abort any pending executions
                // 不允许执行,但如果有缓存,则我们允许添加后在缓存的环境下执行新添加的回调
                lock: function() {
                    locked = queue = [];
                    if ( !memory && !firing ) {
                        list = memory = "";
                    }
                    return this;
                },
                locked: function() {
                    return !!locked;
                },
    
                // Call all callbacks with the given context and arguments
                // 为fire附带了一个上下文来调用fire函数,
                fireWith: function( context, args ) {
                    if ( !locked ) {
                        args = args || [];
                        args = [ context, args.slice ? args.slice() : args ];
                        queue.push( args );
                        if ( !firing ) {
                            fire();
                        }
                    }
                    return this;
                },
    
                // Call all the callbacks with the given arguments
                fire: function() {
                    self.fireWith( this, arguments );
                    return this;
                },
    
                // To know if the callbacks have already been called at least once
                fired: function() {
                    return !!fired;
                }
            };
    
        return self;
    };
    ######jQuery.Deferred###物件#########jQuery.Deferred###物件是一個工廠函數,傳回一個用於非同步或同步呼叫的###deferred###對象,支援鍊式呼叫、回呼函數佇列,並且能針對返回的狀態不同執行不同的回呼。它類似於###ES6###提供的###Promise###對象,提供9個主要的方法:##############done###: 操作成功響應時的回呼函數(同步或非同步,以下相同)###
  2. fail: 操作失败响应时的回调函数

  3. progress: 操作处理过程中的回调函数

  4. resolve: 通过该方法解析该操作为成功状态,调用done

  5. reject: 通过该方法解析该操作为失败状态,调用fail

  6. notify: 通过该方法解析该操作为执行过程中,调用progress

  7. then: 设置回调的简写,接收三个参数,分别是done/fail/progress

  8. always: 设置必须执行的回调,无论是done还是fail

  9. promise: 返回一个受限制的Deferred对象,不允许外部直接改变完成状态

它的实现思想是创建一个对象,包含不同状态下回调函数的队列,并在状态为失败或成功后不允许再次改变。通过返回的Deferred对象进行手动调用resolve/reject/notify方法来控制流程。

看一个实例(纯属胡扯,不要当真)。我们需要从间谍卫星返回的数据用不同的算法来进行解析,如果解析结果信号强度大于90%,则证明该数据有效,可以被解析;如果强度小于10%,则证明只是宇宙噪音;否则,证明数据可能有效,换一种算法解析:

// 我们封装Deferred产生一个promise对象,其不能被外部手动解析,只能内部确定最终状态
asynPromise = function () {
    let d = $.Deferred();
    (function timer() {
        setTimeout(function () {
            // 产生随机数,代替解析结果,来确定本次的状态
            let num = Math.random();
            if (num > 0.9) {
                d.resolve();    // 解析成功
            } else if (num < 0.1) {
                d.reject();    // 解析失败
            } else {
                d.notify();    // 解析过程中
            }
            setTimeout(timer, 1000);    // 持续不断的解析数据
        }, 1000);
    })();
    // 如果不返回promise对象,则可以被外部手动调整解析状态
    return d.promise();
};

// then方法的三个参数分别代表完成、失败、过程中的回调函数
asynPromise().then(function () {
    console.log(&#39;resolve success&#39;);
}, function () {
    console.log(&#39;reject fail&#39;);
}, function () {
    console.log(&#39;notify progress&#39;);
});

// 本地执行结果(每个人的不一样,随机分布,但最后一个一定是success或fail)
notify progress
notify progress
notify progress
notify progress
notify progress
reject fail    // 后面不会再有输出,因为一旦解析状态为success或fail,则不会再改变

除了上面的主要功能,还提供了notifyWith/resolveWith/rejectWith/state辅助方法。

其所有的源码实现和注释为:

Deferred: function( func ) {
        var tuples = [
                // action, add listener, callbacks,
                // ... .then handlers, argument index, [final state]
                // 用于后面进行第一个参数绑定调用第二个参数,第三个和第四个参数分别是其不同的回调函数队列
                [ "notify", "progress", jQuery.Callbacks( "memory" ),
                    jQuery.Callbacks( "memory" ), 2 ],
                [ "resolve", "done", jQuery.Callbacks( "once memory" ),
                    jQuery.Callbacks( "once memory" ), 0, "resolved" ],
                [ "reject", "fail", jQuery.Callbacks( "once memory" ),
                    jQuery.Callbacks( "once memory" ), 1, "rejected" ]
            ],
            state = "pending",
            promise = {
                state: function() {
                    return state;
                },
                // 同时添加done和fail句柄
                always: function() {
                    deferred.done( arguments ).fail( arguments );
                    return this;
                },
                "catch": function( fn ) {
                    return promise.then( null, fn );
                },
                then: function( onFulfilled, onRejected, onProgress ) {
                    var maxDepth = 0;
                    function resolve( depth, deferred, handler, special ) {
                        return function() {
                            var that = this,
                                args = arguments,
                                mightThrow = function() {
                                    var returned, then;

                                    // Support: Promises/A+ section 2.3.3.3.3
                                    // https://promisesaplus.com/#point-59
                                    // Ignore double-resolution attempts
                                    if ( depth < maxDepth ) {
                                        return;
                                    }

                                    returned = handler.apply( that, args );

                                    // Support: Promises/A+ section 2.3.1
                                    // https://promisesaplus.com/#point-48
                                    if ( returned === deferred.promise() ) {
                                        throw new TypeError( "Thenable self-resolution" );
                                    }

                                    // Support: Promises/A+ sections 2.3.3.1, 3.5
                                    // https://promisesaplus.com/#point-54
                                    // https://promisesaplus.com/#point-75
                                    // Retrieve `then` only once
                                    then = returned &&

                                        // Support: Promises/A+ section 2.3.4
                                        // https://promisesaplus.com/#point-64
                                        // Only check objects and functions for thenability
                                        ( typeof returned === "object" ||
                                            typeof returned === "function" ) &&
                                        returned.then;

                                    // Handle a returned thenable
                                    if ( jQuery.isFunction( then ) ) {

                                        // Special processors (notify) just wait for resolution
                                        if ( special ) {
                                            then.call(
                                                returned,
                                                resolve( maxDepth, deferred, Identity, special ),
                                                resolve( maxDepth, deferred, Thrower, special )
                                            );

                                        // Normal processors (resolve) also hook into progress
                                        } else {

                                            // ...and disregard older resolution values
                                            maxDepth++;

                                            then.call(
                                                returned,
                                                resolve( maxDepth, deferred, Identity, special ),
                                                resolve( maxDepth, deferred, Thrower, special ),
                                                resolve( maxDepth, deferred, Identity,
                                                    deferred.notifyWith )
                                            );
                                        }

                                    // Handle all other returned values
                                    } else {

                                        // Only substitute handlers pass on context
                                        // and multiple values (non-spec behavior)
                                        if ( handler !== Identity ) {
                                            that = undefined;
                                            args = [ returned ];
                                        }

                                        // Process the value(s)
                                        // Default process is resolve
                                        ( special || deferred.resolveWith )( that, args );
                                    }
                                },

                                // Only normal processors (resolve) catch and reject exceptions
                                // 只有普通的process能处理异常,其余的要进行捕获,这里不是特别明白,应该是因为没有改最终的状态吧
                                process = special ?
                                    mightThrow :
                                    function() {
                                        try {
                                            mightThrow();
                                        } catch ( e ) {

                                            if ( jQuery.Deferred.exceptionHook ) {
                                                jQuery.Deferred.exceptionHook( e,
                                                    process.stackTrace );
                                            }

                                            // Support: Promises/A+ section 2.3.3.3.4.1
                                            // https://promisesaplus.com/#point-61
                                            // Ignore post-resolution exceptions
                                            if ( depth + 1 >= maxDepth ) {

                                                // Only substitute handlers pass on context
                                                // and multiple values (non-spec behavior)
                                                if ( handler !== Thrower ) {
                                                    that = undefined;
                                                    args = [ e ];
                                                }

                                                deferred.rejectWith( that, args );
                                            }
                                        }
                                    };

                            // Support: Promises/A+ section 2.3.3.3.1
                            // https://promisesaplus.com/#point-57
                            // Re-resolve promises immediately to dodge false rejection from
                            // subsequent errors
                            if ( depth ) {
                                process();
                            } else {

                                // Call an optional hook to record the stack, in case of exception
                                // since it&#39;s otherwise lost when execution goes async
                                if ( jQuery.Deferred.getStackHook ) {
                                    process.stackTrace = jQuery.Deferred.getStackHook();
                                }
                                window.setTimeout( process );
                            }
                        };
                    }

                    return jQuery.Deferred( function( newDefer ) {

                        // progress_handlers.add( ... )
                        tuples[ 0 ][ 3 ].add(
                            resolve(
                                0,
                                newDefer,
                                jQuery.isFunction( onProgress ) ?
                                    onProgress :
                                    Identity,
                                newDefer.notifyWith
                            )
                        );

                        // fulfilled_handlers.add( ... )
                        tuples[ 1 ][ 3 ].add(
                            resolve(
                                0,
                                newDefer,
                                jQuery.isFunction( onFulfilled ) ?
                                    onFulfilled :
                                    Identity
                            )
                        );

                        // rejected_handlers.add( ... )
                        tuples[ 2 ][ 3 ].add(
                            resolve(
                                0,
                                newDefer,
                                jQuery.isFunction( onRejected ) ?
                                    onRejected :
                                    Thrower
                            )
                        );
                    } ).promise();
                },

                // Get a promise for this deferred
                // If obj is provided, the promise aspect is added to the object
                // 通过该promise对象返回一个新的扩展promise对象或自身
                promise: function( obj ) {
                    return obj != null ? jQuery.extend( obj, promise ) : promise;
                }
            },
            deferred = {};

        // Add list-specific methods
        // 给promise添加done/fail/progress事件,并添加互相的影响关系,并为deferred对象添加3个事件函数notify/resolve/reject
        jQuery.each( tuples, function( i, tuple ) {
            var list = tuple[ 2 ],
                stateString = tuple[ 5 ];

            // promise.progress = list.add
            // promise.done = list.add
            // promise.fail = list.add
            promise[ tuple[ 1 ] ] = list.add;

            // Handle state
            // 只有done和fail有resolved和rejected状态字段,给两个事件添加回调,禁止再次done或者fail,锁住progress不允许执行回调
            if ( stateString ) {
                list.add(
                    function() {

                        // state = "resolved" (i.e., fulfilled)
                        // state = "rejected"
                        state = stateString;
                    },

                    // rejected_callbacks.disable
                    // fulfilled_callbacks.disable
                    tuples[ 3 - i ][ 2 ].disable,

                    // progress_callbacks.lock
                    tuples[ 0 ][ 2 ].lock
                );
            }

            // progress_handlers.fire
            // fulfilled_handlers.fire
            // rejected_handlers.fire
            // 执行第二个回调列表
            list.add( tuple[ 3 ].fire );

            // deferred.notify = function() { deferred.notifyWith(...) }
            // deferred.resolve = function() { deferred.resolveWith(...) }
            // deferred.reject = function() { deferred.rejectWith(...) }
            // 绑定notify/resolve/reject的事件,实际执行的函数体为加入上下文的With函数
            deferred[ tuple[ 0 ] ] = function() {
                deferred[ tuple[ 0 ] + "With" ]( this === deferred ? undefined : this, arguments );
                return this;
            };

            // deferred.notifyWith = list.fireWith
            // deferred.resolveWith = list.fireWith
            // deferred.rejectWith = list.fireWith
            deferred[ tuple[ 0 ] + "With" ] = list.fireWith;
        } );

        // Make the deferred a promise
        // 将deferred扩展为一个promise对象
        promise.promise( deferred );

        // Call given func if any
        // 在创建前执行传入的回调函数进行修改
        if ( func ) {
            func.call( deferred, deferred );
        }

        // All done!
        return deferred;
    },

jQuery.when方法

$.when()提供一种方法执行一个或多个函数的回调函数。如果传入一个延迟对象,则返回该对象的Promise对象,可以继续绑定其余回调,在执行结束状态之后也同时调用其when回调函数。如果传入多个延迟对象,则返回一个新的master延迟对象,跟踪所有的聚集状态,如果都成功解析完成,才调用其when回调函数;如果有一个失败,则全部失败,执行错误回调。

其使用方法:

$.when($.ajax("/page1.php"), $.ajax("/page2.php"))
  .then(myFunc, myFailure);

其所有源码实现和注释为(能力有限,有些地方实在不能准确理解执行流程):

// 给when传递的对象绑定master.resolve和master.reject,用于聚集多异步对象的状态
function adoptValue( value, resolve, reject, noValue ) {
    var method;
    try {
        // Check for promise aspect first to privilege synchronous behavior
        // 如果when传入的参数promise方法可用,则封装promise并添加done和fail方法调用resolve和reject
        if ( value && jQuery.isFunction( ( method = value.promise ) ) ) {
            method.call( value ).done( resolve ).fail( reject );

        // Other thenables
        // 否则,就判断传入参数的then方法是否可用,如果可用就传入resolve和reject方法
        } else if ( value && jQuery.isFunction( ( method = value.then ) ) ) {
            method.call( value, resolve, reject );

        // Other non-thenables
        // 如果均不可用,则为非异步对象,直接resolve解析原值
        } else {

            // Control `resolve` arguments by letting Array#slice cast boolean `noValue` to integer:
            // * false: [ value ].slice( 0 ) => resolve( value )
            // * true: [ value ].slice( 1 ) => resolve()
            resolve.apply( undefined, [ value ].slice( noValue ) );
        }

    // For Promises/A+, convert exceptions into rejections
    // Since jQuery.when doesn&#39;t unwrap thenables, we can skip the extra checks appearing in
    // Deferred#then to conditionally suppress rejection.
    } catch ( value ) {

        // Support: Android 4.0 only
        // Strict mode functions invoked without .call/.apply get global-object context
        // 一个安卓4.0的bug,这里不做阐释
        reject.apply( undefined, [ value ] );
    }
}

// Deferred helper
    when: function( singleValue ) {
        var
            // count of uncompleted subordinates
            remaining = arguments.length,

            // count of unprocessed arguments
            i = remaining,

            // subordinate fulfillment data
            resolveContexts = Array( i ),
            resolveValues = slice.call( arguments ),

            // the master Deferred
            master = jQuery.Deferred(),

            // subordinate callback factory
            // 将每一个响应的环境和值都保存到列表里,在全部完成后统一传给主Promise用于执行
            updateFunc = function( i ) {
                return function( value ) {
                    resolveContexts[ i ] = this;
                    resolveValues[ i ] = arguments.length > 1 ? slice.call( arguments ) : value;
                    if ( !( --remaining ) ) {
                        master.resolveWith( resolveContexts, resolveValues );
                    }
                };
            };

        // Single- and empty arguments are adopted like Promise.resolve
        // 如果只有一个参数,则直接将其作为master的回调
        if ( remaining <= 1 ) {
            adoptValue( singleValue, master.done( updateFunc( i ) ).resolve, master.reject,
                !remaining );

            // Use .then() to unwrap secondary thenables (cf. gh-3000)
            if ( master.state() === "pending" ||
                jQuery.isFunction( resolveValues[ i ] && resolveValues[ i ].then ) ) {

                return master.then();
            }
        }

        // Multiple arguments are aggregated like Promise.all array elements
        // 多参数时,进行所有参数的解析状态聚合到master上
        while ( i-- ) {
            adoptValue( resolveValues[ i ], updateFunc( i ), master.reject );
        }

        return master.promise();
    }

后续

本来想把jQuery.DeferredjQuery.ajax以及ES6Promise对象给统一讲一下,结果发现牵涉的东西太多,每一个都可以单独写一篇文章,怕大家说太长不看,这里先写第一部分jQuery.Deferred吧,后续再补充另外两篇。

jQuery的文档很容易,使用也很方便,但其实真正想要讲好很复杂,更不要说写篇源码分析文章了。真的是努力理解设计者的思路,争取每行都能理解边界条件,但踩坑太少,应用场景太少,确实有很大的疏漏,希望大家能够理解,不要偏听一面之词。


以上是jQuery原始碼解析Deferred非同步物件的方法的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn