深入解析jQuery中的延时对象的概念
第一个作用,解决时序以及动态添加执行函数的问题。
function a(){alert(1)};
function b(){alert(2)};
function c(){alert(3)};
a();
setTimeout(function(){b();},0);
c();
很明显函数执行顺序是a->c->b,而不是按照函数添加的顺序执行。如果我要保证函数按顺序执行,那么c 必须写紧跟在b后面执行
setTimeout(function(){b(); c();},0);
如果添加的函数按顺序执行,比如添加在b后面的函数都在b执行后执行(如果b已经执行过了,那么久马上执行)。如果按照这种方式,我们必须在执行之前就知晓说有要执行的函数,然后都加到setTimeout里面去,这个限制非常可恶。我希望的是即使b执行过了,我后面还能动态的添加函数并得到执行。比如
var defer = $.Deferred(); //构建异步对象
function a(){alert(1)};
function b(){alert(2)};//添加函数defer.done(a);//添加函数defer.done(b);
setTimeout(function(){ defer.resolve();//alert(1),alert(2)},0);
//添加函数defer.done(function c(){alert(3)});//马上执行出结果alert(3)
第二,解决参数传递的问题,所有的执行函数需要的参数相同,我希望我只传递一个参数就将所有的结果执行出来,特别是后续添加的函数执行我也希望使用原来的参数。
function a(value){alert("a = " value)};
function b(value){alert("b = " value)};
var list = [],
val = 0;;
list[list.length] = a;
list[list.length] = b;
function runList(listArray,value){
for(var i = 0; i < listArray.length; i ){
listArray[i](value);
}
listArray.length = 0;
}
val = 5;
runList(list,val );//执行a = 5;b = 5;
list[list.length] = function c(value){alert("c = " value)};
runList(list,val);//执行结果c = 5
在保证runList函数有自己的作用域,不使用外部变量的情况下,那么我们每次执行runList都需要重新传递list和val这个变量。这种重复劳动没有任何意义。看看deferred的处理方式
var defer = $.Deferred(); //构建异步对象
function a(value){alert("a = " value)};
function b(value){alert("b = " value)};
defer.done(a,b);//添加函数
defer.resolve(5);//执行结果a = 5; b = 5;
defer.done(function c(value){alert("c = " value)});//添加函数,执行结果c = 5
首先你要承认的是代码简化和可读性都高很多,其次,传递的执行参数值5只需要一次便足够,哪怕是后面添加的函数c也会用先前传递过来的参数执行。
Deferred函数将Callbacks函数抽离出去以后Deferred函数变得很精简。如下
jQuery.extend({
Deferred : function(){…}
when : function(){…}
)}
其中Deferred的整体结构如下
Deferred: function( func ) {
var tuples = [
// action, add listener, listener list, final state
[ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ],
[ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ],
[ "notify", "progress", jQuery.Callbacks("memory") ]
],
state = "pending",
promise = {
state: function() {…},
always: function() {…},
then: function( /* fnDone, fnFail, fnProgress */ ) {…},
promise: function( obj ) {…}
},
deferred = {};
//兼容老版本
promise.pipe = promise.then;
// 添加promise的三个方法[done | fail | progress]方法 // 添加deferred的六个方法[resolve |resolveWith | reject | rejectWith | notify | notifyWith]
jQuery.each( tuples, function( i, tuple ) {
...
});
// Make the deferred a promise
promise.promise( deferred );
...
return deferred;
}
主要做了几个工作。
- 定义promise的几个对外接口state/always/then/promise
- 通过tuples初始化deferred的几个接口resolve/resolveWith/reject/rejectWith/notify /notifyWith,并且为promise定义了另外的三个接口done/fail/progress。其中会被添加到这些个接口都与tuples创建的三个Callbacks有关。后续再详解。
- 使用promise.promise拓展deferred返回。我们看一下这个返回有哪些个方法
deferred.done(fns) |
将回调添加到成功回调列表(doneCallbacks),当Deferred(延迟)对象解决(调用resolve)时会执行成功回调列表中的所有函数。参数fns可以是函数,也可以是函数数组 |
deferred.fail(fns) |
将回调添加到失败回调列表(failCallbacks),当Deferred(延迟)对象拒绝(调用reject)时会执行失败回调列表中的所有函数。参数fns可以是函数,也可以是函数数组 |
deferred.always(fns) |
将回调添加到成功回调列表(doneCallbacks和失败回调列表(failCallbacks)。确保当Deferred(延迟)对象解决(调用resolve)或拒绝(调用reject)时,都会执行到该回调。参数fns可以是函数,也可以是函数数组 |
deferred.progress(fns) |
将回调添加到进度回调列表(progressCallbacks),当Deferred(延迟)对象生成进度通知(调用notify)时,执行进度回调列表中的所有函数。参数fns可以是函数,也可以是函数数组 |
deferred.resolve(args) |
解决Deferred(延迟)对象,并根据给定的args参数执行成功回调列表(doneCallbacks)的所有函数。 |
deferred.resolveWith(context, args) |
解决Deferred(延迟)对象,并根据给定的 context和args参数执行成功回调列表(doneCallbacks)的所有函数。这是jQuery.Callbacks的内部方法fireWith的引用,deferred.resolve()方法内部调用该方法来实现。不建议外部使用。 |
deferred.reject(args) |
拒绝Deferred(延迟)对象,并根据给定的args参数调用失败回调列表(failCallbacks)中的所有函数。 |
deferred.rejectWith(context, args) |
拒绝Deferred(延迟)对象,并根据给定的 context和args参数执行失败回调列表(failCallbacks)的所有函数。这是jQuery.Callbacks的内部方法fireWith的引用,deferred.reject()方法内部调用该方法来实现。不建议外部使用。 |
deferred.notify(args) |
根据给定的 args参数 调用Deferred(延迟)对象上进度回调列表(progressCallbacks)的所有函数。 |
deferred.notifyWith(contex,args) |
根据给定的上下文(context)和args递延调用Deferred(延迟)对象上进度回调列表(progressCallbacks )上的所有函数。这是jQuery.Callbacks的内部方法fireWith的引用,deferred.notify()方法内部调用该方法来实现。不建议外部使用。 |
deferred.state() |
确定一个Deferred(延迟)对象的当前状态。"pending"、"resolved"、"rejected" |
deferred.pipe()/deferred.then() |
当Deferred(延迟)对象解决(resolve)、拒绝(reject)或生成进度(notify),调用相应回调列表的所有函数。 |
jQuery.Deferred() |
一个构造函数,返回一个延时对象。 |
jQuery.when(subordinate /* , ..., subordinateN */) |
提供一种方法来执行一个或多个对象的回调函数, Deferred(延迟)对象通常表示异步事件。 |
.promise() |
返回一个 Promise 对象用来观察当某种类型的所有行动绑定到集合,排队与否还是已经完成 |
a. $.Deferred()详解
关键源码如下
Deferred: function( func ){
var tuples = [
// 执行名, 添加监听器(回调), 监听列表(回调列表), 最终状态
[ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ],
[ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ],
[ "notify", "progress", jQuery.Callbacks("memory") ]
],
state = "pending",
promise = {
...
promise: function( obj ) {
return obj != null ? jQuery.extend( obj, promise ) : promise;
}
},
deferred = {};
// 添加列表指定的方法
jQuery.each( tuples, function( i, tuple ) {
var list = tuple[ 2 ],
stateString = tuple[ 3 ];
// promise[ done | fail | progress ] = list.add
promise[ tuple[1] ] = list.add;
// 只有tuples[0]和tuples[1]进入该分支
if ( stateString ) {
//给Callbacks对象添加回调列表
list.add(function() {
// state = [ resolved | rejected ]
state = stateString;
// [ reject_list | resolve_list ].disable; progress_list.lock
}, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock );
}
// deferred[ resolve | reject | notify ]
deferred[ tuple[0] ] = function() {
deferred[ tuple[0] "With" ]( this === deferred ? promise : this, arguments );
return this;
};
deferred[ tuple[0] "With" ] = list.fireWith;
});
// 给deferred添加promise的函数接口
promise.promise( deferred );
... return deferred;
}
我们把tuples[index][2]中的jQuery.Callbacks返回的对象所代表的回调列表分别取名叫做doneCallbacks(成功回调列表),failCallbacks(失败回调列表),progressCallbacks(进度回调列表)。则到函数执行到promise.promise( deferred );之前。promise变量变成了
promise = {
state: function() {…},
always: function() {…},
then: function( /* fnDone, fnFail, fnProgress */ ) {…},
promise: function( obj ) {…},
done: doneCallbacks.add,
fail: failCallbacks.add,
progress: progressCallbacks.add
}
deferred变量变成了
deferred = {
resolve: function() {
deferred["resolveWith" ]( this === deferred ? promise : this, arguments );
return this;
},
resolveWith: doneCallbacks.fireWith,
reject: function() {
deferred["rejectWith" ]( this === deferred ? promise : this, arguments );
return this;
},
rejectWith: failCallbacks.fireWith,
notify: function() {
deferred["notifyWith" ]( this === deferred ? promise : this, arguments );
return this;
},
notifyWith: progressCallbacks.fireWith
}
初始化结束后doneCallbacks中回调队列如下
[
function(){state = “resolved”:},
failCallbacks.disable,
progressCallbacks.lock
]
初始化结束后failCallbacks中回调队列如下
[
function(){state = “rejected”;},
doneCallbacks.disable,
progressCallbacks.lock
]
初始化结束后progressCallbacks的回调列表中没有任何回调
需要注意的是doneCallbacks、failCallbacks的设置是”once memory”,progressCallbacks设置是”memory”
这样,应当好理解Deferred的原理了。我们使用done/fail是”once memory”设置下的add。意味着如果如果先执行了resolve/ reject方法,后调用done/fail则会直接执行回调。
eg:
var defer = $.Deferred(); //构建异步对象
//执行doneCallbacks的回调,保存场景,这个时候doneCallbacks有三个回调函数,依序执行。其中第二个回//调禁用failCallbacks(禁用reject/rejectWith),第三个回调锁progressCallbacks(锁notify/notifyWith)
defer.resolve( 5 );
defer.done(function( value ) {
console.log('打印出值',value)
});//doneCallbacks.add,立刻执行回调
如果是先调用done/fail,后执行resolve/reject。根据”once memory”将会先添加回调,然后fire。
eg:
var defer = $.Deferred(); //构建异步对象
//1秒钟后执行doneCallbacks回调,保存场景。这个时候的doneCallbacks有四个回调函数,依序执行
setTimeout(function(){
defer.resolve( 5 );
},1000);
//根据”once memory”,第一次fire之前会把回调添加到doneCallbacks中。等待调用
defer.done(function( value ) {console.log('打印出值',value)});
当延迟对象被 resolved 时,任何通过 deferred.then或deferred.done 添加的 doneCallbacks,都会被调用。回调函数的执行顺序和它们被添加的顺序是一样的。传递给 deferred.resolve() 的 args 参数,会传给每个回调函数。当延迟对象进入 resolved 状态后,再添加的任何 doneCallbacks,当它们被添加时,就会被立刻执行,并带上传入给 .resolve()的参数
b. Deferred.promise.then详解
API是这么定义的:
deferred.then( doneFilter [, failFilter ] [, progressFilter ] )
从jQuery 1.8开始, 方法返回一个新的promise(承诺),通过一个函数,可以过滤deferred(延迟)的状态和值。替换现在过时的deferred.pipe()方法。 doneFilter 和 failFilter函数过滤原deferred(延迟)的解决/拒绝的状态和值。 progressFilter 函数过滤器的任何调用到原有的deferred(延迟)的notify 和 notifyWith的方法。
这些过滤器函数可以返回一个新的值传递给的 promise(承诺)的.done() 或 .fail() 回调,或他们可以返回另一个观察的对象(递延,承诺等)传递给它的解决/拒绝的状态和值promise(承诺)的回调。
如果过滤函数是空,或没有指定,promise(承诺)将得到与原来值相同解决(resolved)或拒绝(rejected)。
源码解析:
then: function( /* fnDone, fnFail, fnProgress */ ) {
var fns = arguments;
return jQuery.Deferred(function( newDefer ) {
//遍历tuples,将fnDone, fnFail, fnProgress分别绑定到tuples[0-2][2]的回调列表上
jQuery.each( tuples, function( i, tuple ) {
var action = tuple[ 0 ],//resolve/reject/notify
fn = jQuery.isFunction( fns[ i ] ) && fns[ i ];
//执行deferred[ done | fail | progress ] 函数,将回调fnDone, fnFail, fnProgress分别添加到回调列表
//需要明确的是这些回调函数是添加到deferred中的,不是newDefer中的。
deferred[ tuple[1] ](function() {
//执行fnDone, fnFail, fnProgress函数
var returned = fn && fn.apply( this, arguments );
//如果返回的是Deferred对象或是Deferred.promise对象
if ( returned && jQuery.isFunction( returned.promise ) ) {
//当Deferred(延迟)对象解决/拒绝/生成进度通知时
//调用添加处理程续resolve/reject/notify
returned.promise()
.done( newDefer.resolve )
.fail( newDefer.reject )
.progress( newDefer.notify );
} else {
//执行resolveWith/rejectWith/notifyWith函数执行所有剩余回调
newDefer[ action "With" ]( this === promise ? newDefer.promise() : this, fn ? [ returned ] : arguments );
}
});
});
fns = null;
}).promise();
}。
我们抓住几点:
返回的是新的promise对象
内部有一个过滤器函数
我们将最外层初始化的延时对象叫做主延时对象mainDeferred,经过mainDeferred.then()方法处理的代码中返回的jQuery.Deferred(...).promise(),不是原来的mainDeferred了,我们把它命名为thenDeferred。thenDeferred初始化的时候有这个一段
// 调用给定的func参数
if ( func ) {
func.call( deferred, deferred );
}
刚好thenDeferred的初始化带有函数参数我们命名为thenParamfunc。thenParamfunc的参数是newDefer,结合上面执行func的代码我们可知,这个newDefer就是thenDeferred嘛,饶了半天。thenParamfunc中做一个操作:
往mainDeferred的三个回调列表中添加回调,每个回调内部会执行mainDeferred.then添加的对应的回调函数。
//deferred是主延时对象mainDeferred deferred[ tuple[1] ](function() { //执行fnDone, fnFail, fnProgress函数 var returned = fn && fn.apply( this, arguments ); ...
});
还需要注意的是当这些回调被调用时,mainDeferred.then添加的回调执行后,会执行下面的代码
newDefer[ action "With" ]( this === promise ? newDefer.promise() : this, fn ? [ returned ] : arguments );
这表明mainDeferred.then生成的延时对象thenDeferred的状态依赖于主延时对象mainDeferred。当mainDeferred解决(resolve),那么thenDeferred也会被解决(resolve),并且mainDeferred.then设置的doneFilter的返回值将作为参数传递给thenDeferred的回调列表。
eg:
var deferred = $.Deferred();
var m = deferred
.done(function(val){console.log('done function ' val)})
.then(function(val){console.log('done then ' val); return 10;},
function(val){console.log('fail then ' val);},
function(val){console.log('progress then' val);})
//主延时对象解决//打印done function 5;done then 5;deferred.resolve(5);
//执行then deferred done 10m.done(function(val){console.log("then deferred done " value)})
现在完整的分析一个例子的数据
eg:
var deferred = $.Deferred();
deferred
.done(function(val){console.log('done function ' val)})
.then(function(val){console.log('done then' val);},
function(val){console.log('fail then' val);},
function(val){console.log('progress then' val);})
deferred.resolve(5);
说明:
deferred.done(...).then(...)执行后,done的回调列表doneCallbacks是下面的样子
doneCallbacks = [ function () {
// state = [ resolved | rejected ]
state = stateString;
// [ reject_list | resolve_list ].disable; progress_list.lock
},
function () {
list = stack = memory = undefined;
return this;
},
function () {
stack = undefined;
if ( !memory ) {
self.disable();
}
return this;
},
function (val){
console.log('done function ' val)
},
function () {
var returned = fn && fn.apply( this, arguments );
if ( returned && jQuery.isFunction( returned.promise ) ) {
returned.promise()
.done( newDefer.resolve )
.fail( newDefer.reject )
.progress( newDefer.notify );
} else {
newDefer[ action "With" ]( this === promise ? newDefer.promise() : this, fn ? [ returned ] : arguments );
}
}]
其中doneCallbacks前面三个是Callbacks自带的,第四个是.done添加的,
第五个这个是.then的第一个参数添加的,对应的fn是function(val){console.log('done then' val);}
fail的回调列表failCallbacks的样式是
failCallbacks = [ function () {
// state = [ resolved | rejected ]
state = stateString;
// [ reject_list | resolve_list ].disable; progress_list.lock
},
function () {
list = stack = memory = undefined;
return this;
},
function () {
stack = undefined;
if ( !memory ) {
self.disable();
}
return this;
},
function () {
var returned = fn && fn.apply( this, arguments );
if ( returned && jQuery.isFunction( returned.promise ) ) {
returned.promise()
.done( newDefer.resolve )
.fail( newDefer.reject )
.progress( newDefer.notify );
} else {
newDefer[ action "With" ]( this === promise ? newDefer.promise() : this, fn ? [ returned ] : arguments );
}
}]
前面三个元素是Callbacks自带的,
第四个是.then的第二个参数添加的,对应的fn是function(val){console.log('fail then' val);}
progress对应回调列表progressCallbacks的样子是
progressCallbacks = [ function () {
var returned = fn && fn.apply( this, arguments );
if ( returned && jQuery.isFunction( returned.promise ) ) {
returned.promise()
.done( newDefer.resolve )
.fail( newDefer.reject )
.progress( newDefer.notify );
} else {
newDefer[ action "With" ]( this === promise ? newDefer.promise() : this, fn ? [ returned ] : arguments );
}
}]
只有一个元素是.then的第三个参数添加的,fn对应为function(val){console.log('progress then' val);})
深入解析jQuery中的延时对象的概念相关推荐
- jQuery课程介绍、Query的介绍、Query初次体验、jQuery再次体验、jQuery中的顶级对象
jQuery课程介绍 <!DOCTYPE html> <html lang="en"> <head><meta charset=" ...
- java 字符串是对象吗_解析Java中的String对象的数据类型
解析Java中的String对象的数据类型 2007-06-06 eNet&Ciweek 1. 首先String不属于8种基本数据类型,String是一个对象. 因为对象的默认值是null,所 ...
- 深入解析JQuery中的isPlainObject()使用方法
本篇文章给大家详细分析了jQuery中的isPlainObject()使用方法,非常不错,写的十分的全面细致,具有一定的参考价值,对此有需要的朋友可以参考学习下.如有不足之处,欢迎批评指正. 说明 j ...
- jQuery的deferred延时对象
1.作用 deferred对象是一个延迟对象,意思是函数延迟到某个点才开始执行,改变执行状态的方法有两个(成功:resolve和失败:reject),分别对应两种执行回调(成功回调函数:done和失败 ...
- 后台ajax调用中字符串到jquery中的json对象和数组对象转换问题
查看jquery文档,我们知道jquery有很多种Ajax调用方法,下面结合springmvc返回的数据,假设返回 的是data ='{"label":"1", ...
- php爬取js对象,php如何用正则解析html中的js对象
有一些html中的数据是页面加载完成后执行的js代码生成的,源数据存储在html的script中,用php来获取可以这样写: $html=<< ...... "article_i ...
- html dom firstchild,解析dom中的children对象数组元素firstChild,lastChild的使用
行1列1 行1列2 行2列1 行2列2 alert(test.children[0].tagName) alert(test.children[1].tagName) document.all.tbl ...
- jQuery-1.9.1源码分析系列(六) 延时对象应用——jQuery.ready
还记不记得jQuery初始化函数jQuery.fn.init中有这样是一个分支 //document ready简便写法$(function(){-}) } else if( jQuery.isFun ...
- jQuery中的$是什么含义?
1. $ 是jQuery的别称(其他名字) 2. $ 是jQuery 中的顶级对象 ,相当于原生js中的window.
最新文章
- Android sqlite 数据库保存Date 类型
- python coding style guide 的快速落地实践——业内python 编码风格就pep8和谷歌可以认作标准...
- STL链式存储结构——————list链表
- html页面获取服务器时间,[html]定时获取服务器时间和本地时间
- Mybatis中 Dao接口和XML文件的SQL如何建立关联
- 十分钟搞懂JSON(JSON对象---JSON字符串---对象 之间的区别)
- oc引导windows蓝屏_人人都会遇到系统蓝屏问题,教大家自已排除蓝屏,学会一辈子受用...
- mysql装完后navicat无法连接_重装mysql后导致Navicat连接失败
- 进程间的通信----管道
- Web请求中同步与异步的区别
- 在debian上安装vmware tools的问题
- Qfile与QTextStream读写文本文件
- BigDecimal 计算余数
- HDU 4293 Groups (线性dp)
- C++ 链表入门习题
- 电脑预览,电脑怎么预览psd格式?
- 关于公布2013年度局青年学术和技术带头人考评与增选结果的通知
- python基础五之for和while
- 虹科喜报 | 虹科技术工程师【国内首批】拿下Redis认证开发者证书!
- Halcon12 HObject与VC++ OpenCV Mat相互转换