四、异步队列Deferred Object2——jQuery.Deferred(func)
方法jQuery.Deferred(func)在jQuery 1.5中引入,在jQuery 1.7中重新实现——改为基于jQuery.Callbacks(flags)实现,该方法返回一个链式工具对象,支持添加多个回调函数到回调函数列表、触发回调函数列表、传播任意同步或异步任务的成功或失败状态等功能。在后文中,把返回的链式工具对象称为“异步队列”。
方法jQuery.Deferred(func)在jQuery.Callbacks(flags)的基础上,为回调函数增加了状态,提供了多个回调函数列表。可以通过调用方法deferred.done/fail/progress/then/always()添加成功回调函数、失败回调函数、消息回调函数到对应的成功、失败、消息回调函数列表中,并且可通过调用方法deferred.resolve/resolveWith/rejectWith/notify/notifyWith()分别触发成功、失败、消息回调函数列表。
异步队列又三种状态:待定(pending)、成功(resolved)和失败(rejected)。初始时处于待定状态(pending);调用方法deferred.resolve(args)或resolveWith(context, args)将改变异步队列的状态为成功状态(resolved),并且立即执行添加的所有成功回调函数;调用方法deferred.reject(args)或rejectWith(context, args)则改变异步队列的状态为失败状态(rejected),并且立即执行设置的所有失败回调函数。一旦异步队列进入成功或失败状态,就会保持它的状态不变,例如,再次调用deferred.resolve()或deferred.reject()会被忽略。
异步队列进入成功或失败状态后,仍然可以通过调用deferred.done()、deferred.fail()、deferred.then()、deferred.always()添加更多的回调函数,这些回调函数会被立即执行,并传入之前调用方法deferred。sesolve(args)、deferred.resolveWith(context, args)、deferred.reject(args)或deferred.rejectWith(context, args)时传入的参数。
异步队列基于规范CommonJSPromises/A实现,解耦了异步任务和回调函数。当jQuery方法返回一个异步队列或支持异步队列功能的对象时,例如,jQuery.ajax()或jQuery.when(),很多情况下我们只是想要使用方法deferred.then()、deferred.done()和deferred.fail()添加回调函数到异步队列中,而在创建异步队列的代码内部,将会在某个时间点调用deferred.resolve()或deferred.reject()使合适的回调函数执行。
// 代码行3420——3425 function Identity( v ) { return v; } function Thrower( ex ) { throw ex; } // 代码行:3460——3756 jQuery.extend( { Deferred: function( func ) { var tuples = [ // action, add listener, callbacks, // ... .then handlers, argument index, [final state] // once表示回调函数列表只能被触发一次,memory表示会记录上一次触发回调函数列表时的参数,触发后添加的任何回调函数都将用记录的参数值立即调用。 // 添加消息回调函数 [ "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" ] ], // 初始状态。异步队列的初始状态为待定pending。变量state在调用方法deferred.resolve()或deferred.resolveWidth()后被设置为字符串“resolved”,表示成功状态,调用方法deferred.reject()或deferred.rejectWith()后被设置为字符串“rejected”,表示失败状态。 state = "pending", // 异步队列的只读副本 promise = { // deferred.state()用于返回异步队列的状态。 // 如果方法deferred.state()返回“resolved”,意味着deferred.resolve()或者deferred.resolveWith()已经被调用,成功回调函数已经执行或者正在执行;如果方法deferred.state()返回“rejected,意味着deferred.reject()或者deferred.rejectWith()已经被调用,并且失败回调函数已经执行或者正在执行; state: function() { return state; }, // 在便捷方法always()中,采用链式语法依次调用方法done()、fail()重复添加回调函数,并返回this以继续支持链式语法。调用方法done()、fail()时,关键字this始终指向当前异步队列,因此实际上不借用方法apply()也可以用链式语法。 always: function() { deferred.done( arguments ).fail( arguments ); return this; }, // 捕捉 "catch": function( fn ) { return promise.then( null, fn ); }, // Keep pipe for back-compat // 工具方法deferred.pipe()用于过滤当前异步队列的状态和参数,并返回一个新的异步队列的只读副本。当前异步队列被触发时,过滤函数将被调用并把返回值给只读副本。 pipe: function( /* fnDone, fnFail, fnProgress */ ) { var fns = arguments; // 创建一个新的异步队列并传入一个函数作为参数,这个函数参数在新的异步队列返回前被调用。 return jQuery.Deferred( function( newDefer ) { // 在函数参数执行过程中向当前异步队列添加了特殊的成功、失败和消息回调函数。 jQuery.each( tuples, function( i, tuple ) { // Map tuples (progress, done, fail) to arguments (done, fail, progress) var fn = isFunction( fns[ tuple[ 4 ] ] ) && fns[ tuple[ 4 ] ]; // deferred.progress(function() { bind to newDefer or newDefer.notify }) // deferred.done(function() { bind to newDefer or newDefer.resolve }) // deferred.fail(function() { bind to newDefer or newDefer.reject }) deferred[ tuple[ 1 ] ]( function() { // 当当前的异步队列被触发时,添加的特殊回调函数被调用,执行过滤函数,并检测过滤函数的返回值。 // 如果过滤函数又返回一个异步队列或支持异步队列功能的对象(用最新的异步队列表示),则把新的异步队列的方法resolve()、reject()、notify()添加到最新的异步队列中,当最新的异步队列被触发时,新的异步队列中相应状态的回调函数被调用,参数为最新的异步队列的参数。 // 如果过滤函数的返回值不是异步队列或不支持异步队列功能,新的异步队列中相应状态的回调函数被执行,参数为过滤函数的返回值。 var returned = fn && fn.apply( this, arguments ); if ( returned && isFunction( returned.promise ) ) { returned.promise() .progress( newDefer.notify ) .done( newDefer.resolve ) .fail( newDefer.reject ); } else { newDefer[ tuple[ 0 ] + "With" ]( this, fn ? [ returned ] : arguments ); } } ); } ); fns = null; } ).promise(); }, // 便捷方法deferred.always(onFulfilled, onRejected, onProgress)用于将回调函数同时添加到成功回调函数列表doneList和失败回调函数列表failList,即保存两份引用,当异步队列处于成功(resolved)或失败(rejected)状态时被调用。 // 在便捷方法then()中,采用链式语法依次调用方法onFulfilled(), onRejected(), onProgress()逐个添加回调函数,并返回this以继续支持链式语法。 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 ( 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 = 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'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, isFunction( onProgress ) ? onProgress : Identity, newDefer.notifyWith ) ); // fulfilled_handlers.add( ... ) tuples[ 1 ][ 3 ].add( resolve( 0, newDefer, isFunction( onFulfilled ) ? onFulfilled : Identity ) ); // rejected_handlers.add( ... ) tuples[ 2 ][ 3 ].add( resolve( 0, newDefer, isFunction( onRejected ) ? onRejected : Thrower ) ); } ).promise(); }, // Get a promise for this deferred // If obj is provided, the promise aspect is added to the object // 只读副本deferred.promise(obj) // 方法deferred.promise(obj)用于返回当前异步队列的只读副本,或为一个普通JavaScript对象增加只读副本中的方法并返回。只读副本值暴露了添加回调函数和判断状态的方法:done()、fail()、progress()、then()、always()、state()、pipe(),不包含触发执行和改变状态的方法:resolve()、reject()、notify()、resolveWith()、notifyWith() promise: function( obj ) { // 如果没有传入参数obj,则返回当前异步队列的只读副本promise。 // 如果传入了参数obj,则把只读副本promise中的方法添加到参数obj中,从而使得参数obj具有异步队列行为,但是只包含了添加回调函数和判断状态的方法。 return obj != null ? jQuery.extend( obj, promise ) : promise; } }, deferred = {}; // Add list-specific methods // 添加触发成功、失败、消息回调函数列表的方法 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 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, // rejected_handlers.disable // fulfilled_handlers.disable tuples[ 3 - i ][ 3 ].disable, // progress_callbacks.lock tuples[ 0 ][ 2 ].lock, // progress_handlers.lock tuples[ 0 ][ 3 ].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(...) } 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 // 异步队列,只读副本promise中的方法 promise.promise( deferred ); // Call given func if any // 如果传入函数参数func,则调用。调用时指定上下文为异步队列deferred,并把异步队列deferred作为第一个参数传入,这样函数参数func在执行过程中仍然可以调用异步队列deferred的方法。 if ( func ) { func.call( deferred, deferred ); } // All done! // 返回异步队列deferred return deferred; } } );

浙公网安备 33010602011771号