四、异步队列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(); } } );

浙公网安备 33010602011771号