四、异步队列Deferred Object3——jQuery.when(singleValue)

方法jQuery.when(singleValue)提供了基于一个或多个对象的状态来执行回调函数的功能,通常是基于具有异步事件的异步队列。
如果传入多个异步队列,方法jQuery.when()将返回一个新的“主”异步队列的只读副本,这个副本将跟踪所传入的异步队列的最终状态。一旦多有异步队列都变成成功状态,“主”异步队列的成功回调函数将被调用,参数是包含了所有异步队列成功参数的数组;如果其中要给异步队列变为失败状态,主异步队列的失败回调函数将被调用,参数是失败异步队列的失败参数。
如果传入单个异步队列,方法jQuery.when()将返回它的只读副本,可以在只读副本上继续调用添加回调函数的方法(如deferred.then()),当异步队列进入成功或失败状态时,相应状态的回调函数被执行,通常时由创建异步队列的代码触发执行。
方法jQuery.when(singleValue)也可以接受非异步队列作为参数,非异步队列将被当作一个成功状态的异步队列,所有的成功回调函数会被立即执行,上下文是传入的非异步队列。
如果没有传入参数,方法jQuery.when()将创建一个异步队列并返回它的只读副本,等价于jQuery.Deferred().promise()

实现原理
如果调用方法jQuery.when(singleValue)时传入多个异步队列作为参数,则在内部创建一个“主”异步队列,并维护一个计数器。而传入的异步队列参数则称为“子”异步队列。
为了监听子异步队列的成功状态监听函数。当某个子异步队列进入成功状态时,这个特殊的成功状态监听函数被调用,计数器减一,如果计数器变为0,则说明所有异步队列都未成功状态,执行主异步队列上的成功回调函数,参数未含有所有子异步队列的成功参数的数组。
为了监听子异步队列的失败状态,方法jQuery.when(singleValue)把主异步队列的方法reject()添加到每个子异步队列的失败队列上。当某个自已不队列进入失败状态时,主监听函数的方法reject()被调用,参数未失败异步队列的失败参数。
为了监听子异步队列的消息调用,方法jQuery.when(singleValue)在每个子异步队列上添加会记录消息参数的消息监听函数。当某个子异步队列触发消息时,这个特殊的消息监听函数被调用,并记录消息参数,然后触发主异步队列上的消息回调函数,参数为含有所有子异步队列的消息参数的数组。
// 代码行3427——3458
function adoptValue( value, resolve, reject, noValue ) {
    var method;

    try {

        // Check for promise aspect first to privilege synchronous behavior
        // 首先检查promise方面,以特权同步行为
        if ( value && isFunction( ( method = value.promise ) ) ) {
            method.call( value ).done( resolve ).fail( reject );

        // Other thenables
        } else if ( value && isFunction( ( method = value.then ) ) ) {
            method.call( value, resolve, reject );

        // Other non-thenables
        } 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'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
        // 在Android 4.0,严格模式函数调用.call/.apply得不到全局对象上下文
        reject.apply( undefined, [ value ] );
    }
}

jQuery.extend( {

    // 代码行:3758——3805
    // Deferred helper
    when: function( singleValue ) {
        // 参数singleValue在方法内被当作局部变量使用,传入的参数则通过参数对象arguments访问。
        var

            // count of uncompleted subordinates
            // 未完成的原型数目
            remaining = arguments.length,

            // count of unprocessed arguments
            // 未处理参数的数量
            i = remaining,

            // subordinate fulfillment data
            // 数组resolveContexts用于存放每个子异步队列的消息参数。
            resolveContexts = Array( i ),
            // 数组resolveValues用于存放子异步队列的成功参数,初始值为传入的子异步队列。这里借用数组方法slice()把参数对象arguments转换为数组
            resolveValues = slice.call( arguments ),

            // the master Deferred
            // 新创建一个异步队列作为主异步队列
            master = jQuery.Deferred(),

            // subordinate callback factory
            // 函数updateFunc()返回一个用于监听子异步队列成功状态的回调函数,称为成功状态监听函数。在成功状态监听函数中,当子异步队列的方法resolve()或resolveWith()被调用进入成功状态时,都把成功参数放入数组resolveValues中对应的位置,并使计数器remaining减1。变量i的值为子异步队列在数组remaining中的下标,通过闭包机制引用。如果计数器remaining减至0,则说明所有子异步队列都进入了成功状态,立即调用主异步队列的方法resolveWith()触发执行主导异步队列的成功回调函数,指定上下文为主异步队列,指定参数为含有每个子异步队列成功参数的数组remaining。
            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
        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" ||
                isFunction( resolveValues[ i ] && resolveValues[ i ].then ) ) {

                return master.then();
            }
        }

        // Multiple arguments are aggregated like Promise.all array elements
        while ( i-- ) {
            adoptValue( resolveValues[ i ], updateFunc( i ), master.reject );
        }
        // 如果只传入一个异步队列作为参数,则返回它的只读副本
        return master.promise();
    }
} );

 

posted @ 2019-05-28 13:51  道鼎金刚  阅读(264)  评论(0)    收藏  举报