概述
官方地址见:
点击打开链接
测试代码1:(deferred.always)
always参数可以是一个function对象也可以是一个function数组,当Deferred对象resolve或者reject那么该函数会被调用。因为deferred.always()方法返回一个Deferred对象,所以其它的Deferred的方法可以继续调用,也包括继续调用always方法,当Deferred已经reject或者resolve,那么回调函数按照我们添加的顺序被调用,调用参数就是我们提供给reject,resolve,rejectWith,resolveWith的参数
因为$.get方法返回对象是jqXHR对象,他来自于Deferred对象,于是我们可以用deferred.always()来添加成功和失败的回调函数!
$.get( "test.php" ).always(function() {
alert( "$.get completed with success or error callback arguments" );
});
测试代码2:(deferred.done)
done函数的参数可以是function也可以是function数组,当Deferred已经resolve那么就会被调用,调用的顺序就是我们添加的顺序,该方法也是返回Deferred对象,所以适用于Deferred的方法可以链式调用,包括另一个done方法。当Deferred被resolved,那么done方法被调用,调用的参数就是传递给resolve或者resolveWith的参数。
因为get方法返回jqXHR对象,所以我们直接可以用done方法添加成功回调函数.
$.get( "test.php" ).done(function() {
alert( "$.get succeeded" );
});
function fn1() {
$( "p" ).append( " 1 " );
}
function fn2() {
$( "p" ).append( " 2 " );
}
function fn3( n ) {
$( "p" ).append( n + " 3 " + n );
}
//创建deferred对象
var dfd = $.Deferred();
dfd.done().done( [ fn1, fn2 ], fn3, [ fn2, fn1 ] ).done(function( n ) {
$( "p" ).append( n + " we're done." );
});
//点击按钮时候Deferred调用resolve方法!打印结果[1 2 and 3 and 2 1 and we're done.]
$( "button" ).on( "click", function() {
dfd.resolve( "and" );
});
测试代码3:(deferred.fail)
和done方法一致
测试代码4:(deferred.isRejected)
jQuery1.7中已经过时,用deferred.state代替,如果deferred在reject状态返回true。也就是调用了reject或者rejectWith方法的同时fail方法已经完成
测试代码5:(deferred.isResolved)
Query1.7中已经过时,用deferred.state代替,如果deferred在resolved状态返回true.也就是调用了resolve或者resolveWith方法的同时done方法已经完成
deferred有三种状态,pending,reject,resolved.如果你要将deferred状态改为resolved,那么你就要判断当前的状态!
测试代码6:(deferred.notify)
只有Deferred的构建者能够调用该方法,你可以阻止其它的代码修改Deferred的状态,或者通过deferred.promise()返回一个严格状态的Promise对象来记录状态。如果
notify被调用了,那么所有通过deferred.then,deferrred.progess添加的回调都会被调用,每一个回调函数都会传入notify的参数,任何在resolved或者rejected
状态以后调用的notify都会被忽略
测试代码7:(deferred.notifyWith[context,args])
只有Deferred的构建者能够调用该方法,你可以阻止其它的代码修改Deferred的状态,或者通过deferred.promise()返回一个严格状态的Promise对象来记录状态。当notifyWith
被调用,那么所有通过deferred.then,deferrred.progess添加的回调都会被调用,每一个回调函数都会传入notifyWith的参数,任何在resolved或者rejected
状态以后调用的notify都会被忽略
测试代码8:deferred.pipe( [doneFilter ] [, failFilter ] )或者deferred.pipe( [doneFilter ] [, failFilter ] [, progressFilter ] )
jQuery 1.8以后pipe方法已经过时,用deferred.then代替。
//过滤resolve值
var defer = $.Deferred(),
//pipe方法
filtered = defer.pipe(function( value ) {
return value * 2;
});
//传入参数5
defer.resolve( 5 );
filtered.done(function( value ) {
alert( "Value is ( 2*5 = ) 10: " + value );
});
//过滤reject值
var defer = $.Deferred(),
//传入两个参数,第二个是failFilter
filtered = defer.pipe( null, function( value ) {
return value * 3;
});
defer.reject( 6 );
//这里是filtered.fail事件,上面是done事件
filtered.fail(function( value ) {
alert( "Value is ( 3*6 = ) 18: " + value );
});
//链式任务
var request = $.ajax( url, { dataType: "json" } ),
chained = request.pipe(function( data ) {
return $.ajax( url2, { data: { user: data.userId } } );
});
chained.done(function( data ) {
// data retrieved from url2 as provided by the first request
});
测试代码9(deferred.progress( progressCallbacks [, progressCallbacks ] )产生progress事件后回调函数)
当Deferred产生notify或者notifyWith事件时候回调函数,当已经resolve或者reject那么progress不会被调用。当处于resolve或者reject状态时候,任何新添加的progress函数会马上执行!
测试代码10:(deferred.promise)
该方法允许异步函数,从而不打断现在代码的执行。promise对象仅仅暴露了Deferred的一些方法用于添加额外事件或者判断状态,如then, done, fail, always, pipe, progress, state and promise
没有提供改变状态的方法如:(resolve, reject, notify, resolveWith, rejectWith, and notifyWith)。如果提供了参数,那么就会把这些方法封装到参数上面,并且返回,这可以把promise的行为添加到一些已经存在的对象上面.
function asyncEvent() {
var dfd = jQuery.Deferred();
// Resolve after a random interval
setTimeout(function() {
dfd.resolve( "hurray" );
}, Math.floor( 400 + Math.random() * 2000 ) );
// Reject after a random interval
setTimeout(function() {
dfd.reject( "sorry" );
}, Math.floor( 400 + Math.random() * 2000 ) );
// Show a "working..." message every half-second
setTimeout(function working() {
if ( dfd.state() === "pending" ) {
alert("pending");
dfd.notify( "working... " );
setTimeout( working, 500 );
}
}, 1 );
// Return the Promise so caller can't change the Deferred
//返回一个Promise,因此调用者无法在外面改变Deferred对象的状态!
return dfd.promise();
}
// Attach a done, fail, and progress handler for the asyncEvent
//为异步事件添加done,fail,progress事件!
$.when( asyncEvent() ).then(
function( status ) {
alert( status + ", things are going well" );
},
function( status ) {
alert( status + ", you fail this time" );
},
function( status ) {
$( "body" ).append( status );
}
);
//已经存在的obj对象
var obj = {
hello: function( name ) {
alert( "Hello " + name );
}
},
//创建Deferred对象
defer = $.Deferred();
// 使得obj具有promise的方法
defer.promise( obj );
//改变状态为resolved
defer.resolve( "John" );
//将obj当作Promise使用
obj.done(function( name ) {
obj.hello( name ); //"Hello John"。将resolve的参数传入done函数
}).hello( "Karl" ); //"Hello Karl"。该hello方法是这个promise独有的方法
测试代码11:(deferred.reject)
只有Deferred的构建者能够调用该方法,你可以阻止其它的代码修改Deferred的状态,或者通过deferred.promise()返回一个严格状态的Promise对象来记录状态。如果状态变为reject,那么通过
then或者fail添加的方法都会被调用。传入的参数就是调用reject时候传入的参数,如果当Deferred处于reject状态时候添加的失败回调都会马上执行
测试代码12:(deferred.rejectWith)
只有Deferred的构建者能够调用该方法,你可以阻止其它的代码修改Deferred的状态,或者通过deferred.promise()返回一个严格状态的Promise对象来记录状态。如果状态变为reject,那么通过
then或者fail添加的方法都会被调用。传入的参数就是调用rejectWith时候传入的参数,如果当Deferred处于reject状态时候添加的失败回调都会马上执行
测试代码13:(deferred.resolve)
只有Deferred的构建者能够调用该方法,你可以阻止其它的代码修改Deferred的状态,或者通过deferred.promise()返回一个严格状态的Promise对象来记录状态。如果状态变为resolve,那么通过
then或者done添加的方法都会被调用。传入的参数就是调用resolve时候传入的参数,如果当Deferred处于reject状态时候添加的失败回调都会马上执行
测试代码14:(deferred.resolveWith)
只有Deferred的构建者能够调用该方法,你可以阻止其它的代码修改Deferred的状态,或者通过deferred.promise()返回一个严格状态的Promise对象来记录状态。如果状态变为resolve,那么通过
then或者done添加的方法都会被调用。传入的参数就是调用resolveWith时候传入的参数,如果当Deferred处于reject状态时候添加的失败回调都会马上执行
测试代码15:(deferred.state)
具有三种状态:
pending状态,非reslove和reject
resolve状态:resolve或者resolveWith被调用,同时done方法被调用
reject状态:reject或者rejectWith被调用,同时fail方法被调用
测试代码16:(deferred.then( doneCallbacks, failCallbacks [, progressCallbacks ] ))
jQuery1.8以后,then方法返回一个promise对象可以对deferred的状态和值用函数进行过滤,代替过去的pipe方法
//过滤resolve
var filterResolve = function() {
var defer = $.Deferred(),
filtered = defer.then(function( value ) {
return value * 2;
});
defer.resolve( 5 );
filtered.done(function( value ) {
$( "p" ).html( "Value is ( 2*5 = ) 10: " + value );//打印10
});
};
//过滤reject
var defer = $.Deferred(),
filtered = defer.then( null, function( value ) {//fail
return value * 3;
});
defer.reject( 6 );
filtered.fail(function( value ) {
alert( "Value is ( 3*6 = ) 18: " + value );
});
//链式调用
var request = $.ajax( url, { dataType: "json" } ),
chained = request.then(function( data ) {
return $.ajax( url2, { data: { user: data.userId } } );
});
chained.done(function( data ) {
// data retrieved from url2 as provided by the first request
});
测试代码17:(jQuery.Deferred( [beforeStart ] ))测试代码18:(.promise( [type ] [, target ] ))
var div = $( "<div>" );
div.promise().done(function( arg1 ) {
// 马上调用,打印"true"
alert( this === div && arg1 === div );
});
//当所有的动画完成以后,返回的Promise变成resolved状态,包括那些一开始就存在于callback集合中的和那些后来添加的
$( "button" ).on( "click", function() {
$( "p" ).append( "Started..." );
$( "div" ).each(function( i ) {
$( this ).fadeIn().fadeOut( 1000 * ( i + 1 ) );
});
$( "div" ).promise().done(function() {
$( "p" ).append( " Finished! " );
});
});
//用$.when()将返回的Pormise对象变成resolved,.promise可以用于jQuery集合
var effect = function() {
return $( "div" ).fadeIn( 800 ).delay( 1200 ).fadeOut();
};
$( "button" ).on( "click", function() {
$( "p" ).append( " Started... " );
$.when( effect() ).done(function() {
$( "p" ).append( " Finished! " );
});
});
下面我们开始jQuery是怎么完成的,请提前阅读Callbacks相关问题
源码问题1:测试每一个Deferred对应的三个Callbacks作用
var dfd=$.Deferred();
setInterval(function()
{
alert(111);
dfd.resolve();
},1000)
dfd.done(function(){
alert("成功");
}).fail(function()
{
alert("失败");
}).progress(function(){alert("进行中!")})
note:这个例子done中的函数只会执行一次,以后在调用resolve的时候不会触发done方法,reject也是一样!但是如果把上面修改为notify那么一直会交替弹出:[111,进行中]。为什么完成和未完成只能触发一次,而进行中要连续触发?因为成功或者失败是一种状态,然而进行中可以连续触发,这对进度条等有作用!之所以能够实现只会调用一次,因为源码中用的是jQuery.Callbacks("once memory"),
因为once+memory的组合调用了一次就会把list=[]所以下次继续resolve(底层调用fireWith),list已经为空了!没有效果!
源码问题2:(memory在这里有什么作用,复习Callbacks内容)
var cb=$.Callbacks("memory");
cb.add(function(){
alert(1);
});
cb.fire();//点击按钮以后会把新的函数添加入cb中间去,而memory表示不需要自己调用就能执行
//因为add里面会自己执行!会拿着上一次的参数执行,上一次如果没有参数那么就是undefined!
$("input").click(function()
{
cb.add(function()
{
alert(2);
})
});
note:到了这里你应该有了想法了,memory如果没有手动调用的时候会自己调用!那么在上面的例子中,如果我们已经resolve了,也就是list集合中的函数已经全部调用了,那么我们现在重新添加一个done方法会怎么样呢?
立即执行!之所以会这样就是memory记住了上一次的参数,不用手动调用而会立即调用新的函数!(这是为什么我总结:如果没有手动调用,在有memory的情况的下只会用老参数调用新函数;如果手动调用的时候才会发生两个阶段,阶段一就是用老参数调用新函数,阶段二就是用新函数调用所有的函数)这是为什么resolve以后再次添加done方法这个方法会立即执行的原因!这和下面的例子是一样的!
源码问题3:
var dfd=$.Deferred();
setTimeout(function()
{
alert(111);
dfd.resolve();
},1000)
dfd.done(function(){
alert("aaa");
}).fail(function()
{
alert("失败");
}).progress(function(){alert("进行中!")})
$("input").click(function()
{
cb.add(function()
{
alert("bbb");
})
});
//点击的时候状态已经完成了,也就是已经resolve了,当点击按钮的时候马上弹出bbb!只要状态已经完成,那么你再进行done操作的时候会立即执行,
//这就是memory起的作用!
源码问题4:(简单执行)
function aaa()
{
var dfd=$.Deferred();
setTimeout(function()
{
dfd.resolve();
},1000)
}
var newDfd=aaa();
newDfd.done(function(){alert("成功")}).fail(function(){alert("失败");})
// 很显然会弹出"成功",因为resolve会调用底层的fireWith,fireWith就会把集合中的函数全部执行,而resolve对应的Callbacks就是done!
测试问题4:(Deferred对象的状态很容易被修改!)
function aaa()
{
var dfd=$.Deferred();
setTimeout(function()
{
dfd.resolve();
},1000)
return dfd;
}
var newDfd=aaa();
newDfd.done(function(){alert("成功")}).fail(function(){alert("失败");})
newDfd.reject();//上面的定时器还没有执行,失败已经触发了,这时候成功不会调用!说明状态很容易修改掉
测试问题5:(为什么源码中的要封装promise对象,该对象没有resolve,resolveWith等修改状态的方法)
function aaa()
{
var dfd=$.Deferred();
setTimeout(function()
{
dfd.resolve();
},1000)
return dfd.promise();
}
var newDfd=aaa();
newDfd.done(function(){alert("成功")}).fail(function(){alert("失败");})
newDfd.reject();//这时候不能修改状态,还会报错,因为promise对象没有reject!
测试问题6:(pipe方法的使用)
var dfd=$.Deferred();
setTimeout(function()
{
dfd.resolve("hi");
},1000)
//返回一个新的延迟对象!返回值作为延迟对象的resove的参数
var newDfd= dfd.pipe(function(){
return arguments[0]+"xxx"
})
//这个会立即执行,因为上面已经resolve了!
newDfd.done(function(){
alert(arguments[0]);
});
jQuery.Deferred对象源码:
jQuery.extend({
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() {
return state;
},
//不管成功还是失败都是会回调的!也就是把这个函数同时添加如done对应的Callbacks对象中也添加进fail对于的Callbacks对象中
//记住这里的done,fail对应于相应的Callbacks的add方法!
always: function() {
deferred.done( arguments ).fail( arguments );
return this;
},
//分开写,第一个是完成的回调,第二个是失败,第三个是进度
then: function( /* fnDone, fnFail, fnProgress */ ) {
var fns = arguments;
//pipe方法和then方式是同一个方法,所以返回值是一个Defered类型,参见博客中“测试问题6”
//jQuery.Deferred中传入一个函数表示立即执行!
return jQuery.Deferred(function( newDefer ) {
jQuery.each( tuples, function( i, tuple ) {
//假如传入了三个函数,那么第一次i为0,获取到第一个函数也就是成功回调函数
var fn = jQuery.isFunction( fns[ i ] ) && fns[ i ];
// deferred[ done | fail | progress ] for forwarding actions to newDefer
//第一次调用done函数,第二次是fail,第三次是notify函数
//deferred[ tuple[1] ]就是deferred["done"]这就是add方法,也就是把后面的那个函数添加到done里面去执行!
deferred[ tuple[1] ](function() {
//获取第一个成功回调函数的返回值,这里的arguments就是传递给resolve或者reject或者notify的参数!
//之所以arguments是resolve的参数是因为:这里直接在done的Callbacks列表中加入了这个匿名函数,
//如果外面已经resolve了那么这里就会立即执行!
var returned = fn && fn.apply( this, arguments );
//如果返回值是promise的处理逻辑
if ( returned && jQuery.isFunction( returned.promise ) ) {
returned.promise()
.done( newDefer.resolve )
.fail( newDefer.reject )
.progress( newDefer.notify );
} else {
//否则:
newDefer[ tuple[ 0 ] + "With" ]( this === promise ? newDefer.promise() : this, fn ? [ returned ] : arguments );
}
});
});
fns = null;
}).promise();
},
// Get a promise for this deferred
// If obj is provided, the promise aspect is added to the object
//如果没有参数那么返回Deferred对象对应的promise,如果有参数那么就是让参数继承promise所有的方法
//后面用到这里让Deferred继承promise的所有的方法!
promise: function( obj ) {
return obj != null ? jQuery.extend( obj, promise ) : promise;
}
},
deferred = {};
// Keep pipe for back-compat
promise.pipe = promise.then;
// Add list-specific methods
jQuery.each( tuples, function( i, tuple ) {
var list = tuple[ 2 ],
stateString = tuple[ 3 ];
//promise里面封装了三个Callbacks对象,promise的done|faile|progress对应于相应对象的add方法
//done里面的函数会被封装到jQuery.Callbacks("once memory")这个Callbacks对象上面,
//fail会被封装到jQuery.Callbacks("once memory")这个Callbacks对象上面,
//progress会被封装到jQuery.Callbacks("memory")这个Callbacks对象上面,而且这三个Callbacks是互不影响的!
//也就是add时候只是添加到特定的Callbacks里面!
// promise[ done | fail | progress ] = list.add
promise[ tuple[1] ] = list.add;
// Handle state
//触发了未完成那么不能触发完成,触发了完成不可能触发为完成的代码,这里就是用来控制的!
//第一次是resolve,所以tupes[i^1]就是reject,也就是resolve执行了不能再执行reject,即让所有的reject全部调用disable!
//这里要深入理解Callbacks才行,原理为:
//(1)如果是resolve类型那么我把这个reject中的集合jQuery.Callbacks("once memory")全部调用disable
//也就说这个集合里面的list=stack=memory=undefined!(请仔细阅读Callbacks源码)
//(2)而且这个函数是放在最前面的,也就是每一次开始调用的时候他都是最前调用的,以防有人调用reject改变状态!
if ( stateString ) {
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;
};
//(1)Deferred对象的resolveWidth等方法都是对应于Callbacks对象的fireWith方法!
//而且Deferred对象的resolve,reject,notify方法调用的是resolveWith,rejectWith,notifyWidth
//也就是归根到底,resolve方法还是调用fireWidth只是要传进去相应的参数而已!
//从上面的代码可以看到,如果调用resolveWith的调用者是Deferred对象,那么调用fireWith时候传入的第一个参数就是promise
//如果调用resolveWidth的不是Deferred对象,那么调用fireWidth时候的第一个参数就是调用者,第二个参数是resolveWith传入的参数!
//(2)这里也就是说对于resolveWith来说,他对应于jQuery.Callbacks("once memory")的fireWith也就说resolve调用了只会调用这里面的函数的队列
//而这里面函数的队列对应于就是done中间的队列,done中间的函数队列又对应于jQuery.Callbacks("once memory")中add的方法!
deferred[ tuple[0] + "With" ] = list.fireWith;
});
// Make the deferred a promise
promise.promise( deferred );
// Call given func if any
if ( func ) {
func.call( deferred, deferred );
}
// All done!
return deferred;
}
总结:
(1)Deferred对象有三个方法done,fail,notify,他们对应于不同的Callbacks对象的add方法,所以我们在这三个方法里面直接添加函数!如果是done方法相当于直接在done对于的Callbacks中添加了回调函数,构建出一个list集合!那么如何触发这个集合中的函数呢?Deferred对象专门为这个Callbacks准备了resolve,resolveWith等方法(底层调用该Callbacks的fireWith方法),该方法相当于执行通过done方法添加的Callbacks中的所有的函数!(其它函数也是同样的思路!)
(2)回调函数在promise方法上面,但是状态改变的方法全部在deferred对象上!Deferred只是多了修改状态的方法!
(3)如果调用resolveWith的调用者是Deferred对象,那么调用fireWith时候传入的第一个参数就是promise, 如果调用resolveWidth的不是Deferred对象,那么调用fireWidth时候的第一个参数就是调用者,第二个参数是resolveWith传入的参数!
最后
以上就是清秀咖啡为你收集整理的jQuery的Deferred对象测试笔记以及源码分析的全部内容,希望文章能够帮你解决jQuery的Deferred对象测试笔记以及源码分析所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复