一、Deferred对象
Deferred对象就是jQuery的回调函数解决方案。Deferred的对象的含义就是延迟到未来某一个点再执行。它解决了如何处理耗时操作的问题,对那些操作提供了更好的控制
,以及统一的编程接口。
二、用法举例
var dfd= $.Deferred();
dfd.done(function(){
alert("Done");
}).fail(function(){
alert("failed")
}).progress(function(){
alert("progress");
});
dfd.notify();
dfd.resolve();
//dfd.reject();
三、改变了传统$.ajax的写法
传统的ajax请求是
$.ajax({
url:"./test.txt",
success:function(data){
alert(data);
},
error:function(){
alert("error");
}
});
$.ajax("./test.txt").done(function(data){
alert(data);
}).fail(function(){
alert("failed")
});
done相当于是success方法,fail相当于是error方法。
四、源码分析
1.可以传入参数
$.extend({
Deferred:function(func){
if ( func ) {
func.call( deferred, deferred );
}
// All done!
return deferred;
}
})
Deferred对象是扩展到了jquery对象上,是一个工具方法。可以传入参数。当传入参数时,该参数如果是函数就会立即执行。调用Deferred方法,最后返回一个deferred对象。
2.Deferred对象的几个方法和状态
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") ]
],
Deferred有3种类型。事件添加有done 、fail和progress,这三个方法对应着Callbacks对象的add方法。事件触发有resolve、reject和notify,这三种方法对应着Callbacks对象的fire方法。done和resolve代表事件成功,fail和reject对应着事件执行失败,progress和notify对应着进行中。
其中,resolve和reject只触发一次,因为jQuery.Callbacks("once memory"),而notify可以触发多次。事件成功和事件失败,有对应的状态resolved和rejected,事件进行中则没有状态。
那么这是如何对应的呢?
// Add list-specific methods
jQuery.each( tuples, function( i, tuple ) {
var list = tuple[ 2 ], //Callbacks对象
stateString = tuple[ 3 ]; //状态
// promise[ done | fail | progress ] = list.add
promise[ tuple[1] ] = list.add; //给promise对象添加done fail progress方法。
// Handle state
if ( stateString ) { //如果有状态的话,进入if
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 ); //i=0时,i^1=1,也就是说,如果执行了事件成功的done那么,后面的事件失败对应的Callbacks对象就会失效。同时,进度事件锁住,不会被触发。
}
// deferred[ resolve | reject | notify ]
deferred[ tuple[0] ] = function() {
deferred[ tuple[0] + "With" ]( this === deferred ? promise : this, arguments ); //触发fireWith事件
return this;
};
deferred[ tuple[0] + "With" ] = list.fireWith;
});
3.promise对象
3.1 用法举例
一般的Deferred对象有个问题,就是状态会被改变。
var dfd= $.Deferred(),
d,//
wait=function(dfd){
var task=function(){
alert("Done");
dfd.resolve();
}
setTimeout(task,500);
return dfd;
};
d=wait(dfd);
$.when(d)
.done(function(){
alert("Finished")
})
.fail(function(){
alert("Failed");
});
d.resolve();
在外部,改变了dfd对象的状态,那么回调的时候就不会再执行第二次的事件函数。
为了避免Deferred对象的状态被外部改变,jquery提供了deferred.promise()函数,该函数是promise对象的一个方法。
promise.promise( deferred );
promise: function( obj ) {
return obj != null ? jQuery.extend( obj, promise ) : promise;
}
当函数里面有参数时,会被promise对象属性和方法都扩展到入参,如果没有入参,直接返回promise对象本身。而promise对象本身没有resolve和reject方法。不能改变Deferred对象的状态。
var dfd= $.Deferred(),
d,//
wait=function(dfd){
var task=function(){
alert("Done");
dfd.resolve();
}
setTimeout(task,500);
return dfd.promise();
};
d=wait(dfd);
$.when(d)
.done(function(){
alert("Finished")
})
.fail(function(){
alert("Failed");
});
d.resolve();//报错
3.2 源码解析
promise = {
state: function() {
return state;
},
always: function() {
deferred.done( arguments ).fail( arguments );//无论成功还是失败都会执行同一个函数
return this;
},
then: function( /* fnDone, fnFail, fnProgress */ ) {//简写,里面的3个入参分别代表成功、失败和进行中要执行的函数
var fns = arguments;
return jQuery.Deferred(function( newDefer ) {<span style="white-space:pre"> </span> //Deferred()里面的函数会议立即执行,传入的参数时deferred对象
jQuery.each( tuples, function( i, tuple ) {
var fn = jQuery.isFunction( fns[ i ] ) && fns[ i ];
// deferred[ done | fail | progress ] for forwarding actions to newDefer
deferred[ tuple[1] ](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 {//对应Callbacks对象的fireWith方法
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
promise: function( obj ) { //有参数时,把promise对象扩展到入参对象上,没有返回promise对象
return obj != null ? jQuery.extend( obj, promise ) : promise;
}
}