jQuery源码分析(九) 异步队列模块 Deferred 详解
deferred对象就是jquery的回调函数解决方案,它解决了如何处理耗时操作的问题,比如一些ajax操作,动画操作等。(p.s:紧跟上一节:https://www.cnblogs.com/greatdesert/p/11433365.html的内容)
异步队列有三种状态:待定(pending)、成功(resolved)和失败(rejected),初始时处于pending状态
我们可以使用jquery.deferred创建一个异步队列,返回一个对象,该对象含有如下操作:
done(fn/arr) ;添加成功回调函数,当异步队列处于成功状态时被调用,参数同:jquery.callbacks(flags).add(fn/arr)
fail(fn/arr) ;添加失败回调函数,参数同上
progress(fn/arr) ;添加消息回调函数,参数同上
then(donefn/arr,failfn/arr,profn/arr) ;同时添加成功回调函数、失败回调函数和消息回调函数
always(fn/arr) ;添加回调函数到donelist和faillist中,即保存两份引用,当异步队列处于成功或失败状态时被调用 ;参数可以是函数、函数列表
state() ;返回异步队列的当前状态、返回pending、resolved或者rejected
promise(obj) ;如果设置了obj参数对象则为obj对象增加异步队列的方法并返回。如果未设置参数obj,则返回当前deferred对象的只读副本
如果调用$.diferred()创建一个异步队列,返回后的对象可以调用如下几个方法:
writer by:大沙漠 qq:22969969
resolve(args) ;使用指定的参数调用所有的成功回调函数,异步队列进入成功状态,之后就无法再调用
reject(args) ;使用指定的参数调用所有的失败回调函数,异步队列进入失败状态
notify(args) ;使用指定的参数调用所有的消息回调函数
resolvewith(context,args) ;使用指定的上下文和参数调用所有的成功回调函数,异步队列进入成功状态
rejectwith(context,args) ;使用指定的上下文和参数调用所有的失败回调函数,异步队列进入失败状态
notifywith(context,args) ;使用指定的上下文和参数调用所有的消息回调函数
是不是和上一节介绍的callbacks的fire和firewith很像,defferred内部就是通过通过callback()回调函数列表来实现的,例如:
<!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <title>document</title> <script src="http://libs.baidu.com/jquery/1.7.1/jquery.js"></script> </head> <body> <script> function f1(x){console.log('f1 '+x);} function f2(x){console.log('f2 '+x);} function f3(x){console.log('f3 '+x);} var t = $.deferred(); //创建异步队列 t.done(f1).fail(f2).progress(f3); //添加成功回调函数、失败回调函数和消息列表回调函数。等同于t.then(f1,f2,f3); t.notify('test'); //触发所有消息函数列表,输出:f3 test t.resolve('done'); //触发所有成功回调函数,输出:done t.reject('fail'); //没有输出,触发成功回调函数后失败回调函数列表就会被禁用,反过来也如此 t.progress(f1); //输出:f1 test。这是因为消息回调函数之前已经被调用过,保存了上下文和参数了,如果之前没有调用,这里就没有输出。 </script> </body> </html>
输出如下:
源码分析
异步队列内部的实现原理就是通过jquery.callbacks定义三个回调函数列表,分别存储成功、失败、消息回调函数,然后返回一个对象,在该对象上设置done、fail和progress,分别对应这三个回调函数列表的add方法,这样就实现分别添加不同状态的回调函数了
$.deferred是通过jquery.extend函数直接挂载到jquery里的,结构如下:
jquery.extend({ deferred: function( func ) { var donelist = jquery.callbacks( "once memory" ), //成功回调函数列表 faillist = jquery.callbacks( "once memory" ), //失败回调函数列表 progresslist = jquery.callbacks( "memory" ), //消息回调函数列表 ;这三个回调函数列表就是存储我们添加的触发函数的 state = "pending", //初始状态:pending lists = { //后面的方法会遍历变量list中的属性名来为异步队列添加方法deffred.resolve()、resolvewith()、reject()、rejectwith()、notify()和notifywith() resolve: donelist, reject: faillist, notify: progresslist }, promise = { //异步队列的只读副本 /*略*/ }, deferred = promise.promise({}), //异步队列 key; for ( key in lists ) { //添加触发成功、失败、消息回调函数的方法,执行后deferred对象就多了resolve()、resolvewith()、reject()、rejectwith()、notify()和notifywith()方法。 deferred[ key ] = lists[ key ].fire; deferred[ key + "with" ] = lists[ key ].firewith; } // handle state deferred.done( function() { //添加三个成功回调函数,分别用于设置状态为成功(resolved),禁用失败列表函数列表和锁定消息回调函数列表 state = "resolved"; }, faillist.disable, progresslist.lock ).fail( function() { //添加三个失败回调韩素,分别用于设置状态为失败(rejected),禁用成功列表函数列表和锁定消息回调函数列表 state = "rejected"; }, donelist.disable, progresslist.lock ); // call given func if any if ( func ) { func.call( deferred, deferred ); } // all done! return deferred; //返回异步队列deffrred }, //... })
可以看到$.deferred最后返回的是deferred这个布局变量,而deferred是promise.promise({})的返回值,我们来看看promise的实现方法:
promise = { //异步队列的只读副本 done: donelist.add, //添加成功回调函数,引用对应的回调函数列表的callbacks.add(fn/arr)方法,下同 fail: faillist.add, //添加失败回调函数 progress: progresslist.add, //添加消息回调函数 state: function() { //返回异步队列的当前状态:pending、resolved、rejected之一 return state; }, // deprecated isresolved: donelist.fired, isrejected: faillist.fired, then: function( donecallbacks, failcallbacks, progresscallbacks ) { //同时添加成功回调函数、失败回调函数和消息回调函数 deferred.done( donecallbacks ).fail( failcallbacks ).progress( progresscallbacks ); return this; }, always: function() { //添加回调函数到donelist和faillist中,即保存两份引用,当异步队列处于成功或失败状态时被调用 deferred.done.apply( deferred, arguments ).fail.apply( deferred, arguments ); return this; }, pipe: function( fndone, fnfail, fnprogress ) { //过滤当前异步队列的状态和参数,并返回一个新的异步队列的副本,一般用不到 return jquery.deferred(function( newdefer ) { jquery.each( { done: [ fndone, "resolve" ], fail: [ fnfail, "reject" ], progress: [ fnprogress, "notify" ] }, function( handler, data ) { var fn = data[ 0 ], action = data[ 1 ], returned; if ( jquery.isfunction( fn ) ) { deferred[ handler ](function() { returned = fn.apply( this, arguments ); if ( returned && jquery.isfunction( returned.promise ) ) { returned.promise().then( newdefer.resolve, newdefer.reject, newdefer.notify ); } else { newdefer[ action + "with" ]( this === deferred ? newdefer : this, [ returned ] ); } }); } else { deferred[ handler ]( newdefer[ action ] ); } }); }).promise(); }, // get a promise for this deferred // if obj is provided, the promise aspect is added to the object promise: function( obj ) { //如果设置了obj参数对象则为obj对象增加异步队列的方法并返回。如果未设置参数obj,则返回当前deferred对象的只读副本, if ( obj == null ) { //如果obj为空 obj = promise; //则返回promise对象(是上一层作用域链的promise对象) } else { for ( var key in promise ) { //否则遍历promise obj[ key ] = promise[ key ]; //将promise的所有方法、属性保存到obj中 } } return obj; //最后返回obj } },
$.deferred就是对callbacks回调函数列表的管理而已,产生了一个新的$.defferred接口,内部添加了一个state表示异步队列的状态。
推荐阅读
-
jQuery 源码分析(十三) 数据操作模块 DOM属性 详解
-
jQuery 源码分析(十二) 数据操作模块 html特性 详解
-
jQuery 源码分析(十九) DOM遍历模块详解
-
jQuery源码分析之异步队列 Deferred 使用介绍
-
jQuery 源码分析(二十一) DOM操作模块 删除元素 详解
-
jQuery 源码分析(十五) 数据操作模块 val详解
-
jQuery 源码解析(八) 异步队列模块 Callbacks 回调函数详解
-
jQuery源码分析(九) 异步队列模块 Deferred 详解
-
jQuery 源码分析(十六) 事件系统模块 底层方法 详解
-
jQuery 源码分析(十七) 事件系统模块 实例方法和便捷方法 详解