jQuery 延迟对象源码
Deferred延迟对象
jQuery.extend({
Deferred : function () {
;
}
when : function () {
;
}
});
扩展了2个工具方法。
延迟对象,是基于回调函数开发的。
$.Deferred(); -> $.Callbacks();
$.when();
复习一下Callbacks:
例子(1)
var cb = $.Callbacks();
setTimeout(function () {
alert(111);
cb.fire();
}, 1000);
cb.add(function () {
alert(222);
})
//先弹出111 ,然后是222.
延迟对象Deferred
例子(2)
var dfb = $.Deferred();
setTimeout(function () {
alert(111);
dfb.resolve(); //完成
}, 1000);
dfb.done(function () { //成功
alert(222);
})
//先弹出111 ,然后是222.
如果是
例子(3)
setTimeout(function () {
alert(111);
}, 1000);
alert(222);
//这就先弹出222, 在弹出111.
如果有一个需求,就是先弹出111,再弹出222。
如果将alert(222);放入alert(111)后面。也是一个解决办法,
但是这样的办法,存在作用域的问题。就变成内部作用域了。
这个时候选用延迟对象,就是很好的方式了。
例子(4)
var dfb = $.Deferred();
setTimeout(function () {
alert(111);
dfb.reject(); //未完成
}, 1000);
dfb.fail(function () { //失败
alert(222);
})
//先弹出111 ,然后是222.
还有一个进行时:
例子(5)
var dfb = $.Deferred();
setTimeout(function () {
alert(111);
dfb.notify(); //运行时
}, 1000);
dfb.progress(function () { //动态执行
alert(222);
})
一共三组对应:
[ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ],
[ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ],
[ "notify", "progress", jQuery.Callbacks("memory") ]
例子(6)
$.ajax({
url : 'xxx.php',
success : function () {
alert('success');
},
error : function () {
alert('fail');
}
});
//可以通过这个去写。成功的时候触发done,失败触发fail
$.ajax('xxx.php').done(function () {
alert('success');
}).fail(function () {
alert('fail');
});
resolve() 和 reject() -->>> fire()
done() 和 fail() -->>> add()
这个复合数组定义了对应的映射状态。
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") ]
],
这里注意第三个:memory是有记忆功能,而once是只能触发一次。这个在callbacks中我有分析道。
在这里的运行中,是可以多次运行的。因为无论是成功还是失败,都是已经执行结束了。而"notify", "progress"可以执行多次。
数组的三个对,每一行有三个参数。最后一个是创建回调对象,完成回调方法。
这里是对数组进行了匹配和分离。
状态就是fire
回调的方法,就是add
源码:
// Add list-specific methods
jQuery.each( tuples, function( i, tuple ) {
var list = tuple[ 2 ], // 数组的第二项是回调对象。
stateString = tuple[ 3 ]; // 第三项是状态
// promise[ done | fail | progress ] = list.add,
// 这个注释阐述了回调对象的add对应着三个状态:成功,失败和进行中。这就是回调的方法。
promise[ tuple[1] ] = list.add;
// Handle state
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 ]
// 这个延迟对象加入了状态所对应的函数。
// 这个状态,调用的是 fireWith 这个Callback的方法
deferred[ tuple[0] ] = function() {
deferred[ tuple[0] + "With" ]( this === deferred ? promise : this, arguments );
return this;
};
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;
},
例子(7) var dfb = $.Deferred(); setTimeout(function () { alert(111); dfb.resolve(); }, 1000); dfb.done(function () { alert('成功'); }).fail(function () { alert('失败') }) 弹出111后。然后弹出成功。也就证明是resolve对应着done。 如果写入reject,就对应着fail。 例子(8) 解释一下[ "notify", "progress", jQuery.Callbacks("memory") ]与其他两者的不同。 var dfb = $.Deferred(); setInterval(function () { alert(111); dfb.resolve(); }, 1000); dfb.done(function () { alert('成功'); }).fail(function () { alert('失败') }) // 这里的实验结果就是第一次弹出 111, 然后弹出 ‘成功’; // 第二次以后只弹出 111. 对比 var dfb = $.Deferred(); setInterval(function () { alert(111); dfb.notify(); }, 1000); dfb.done(function () { alert('成功'); }).fail(function () { alert('失败') }).progress(function () { alert('进行中。。'); }); // 这个就是多次弹出 111 和 进行中。。。 // 总是成对出现。 状态这个东西,只要一触发,就是结束了,没必要连续触发。 成功就是成功了。失败就是失败了。 在分析一次 memory 的问题。就是记忆功能功能。在之前的callbacks分析过了。 这里在写一次。 例子(9) var cb = $.Callbacks(); cb.add(function () { alert(1); }) cb.fire(); cb.add(function () { alert(2); }) //这里就只能弹出 -->> 1 var cb = $.Callbacks('memory'); cb.add(function () { alert(1); }) cb.fire(); cb.add(function () { alert(2); }) // 这里可以弹出 -->> 1 , 2 在fire的时候,他有记忆功能,把所有的add添加的函数,都调用。 例子(10) <input type="button" value ='点击' id='but' /> var cb = $.Callbacks('memory'); cb.add(function () { alert(1); }); cb.fire(); var but = document.getElementById('but'); but.onclick = function () { cb.add(function () { alert(2); }); } // 这里先执行 1. 然后点击以后,执行 2, //这里都有memery,所以延迟对象,都有记忆功能。 var dfb = $.Deferred(); setTimeout(function () { alert(111); dfb.resolve(); }, 1000); dfb.done(function () { alert('成功 aaa'); }); var but = document.getElementById('but'); but.onclick = function () { dfb.done(function () { alert('bbb'); }); } // 先执行 111 —— >> resolve()执行,然后调用done alert('aaa') // 然后,点击事件的时候,立即执行 bbb。 // 利用这个特性,就第一次不去触发执行,以后每一次都去触发执行的特性。
promise 和 deferred : 两个数组,一个定义了add ,另一个对应着firewith
// Make the deferred a promise
promise.promise( deferred ); //这里就是将 promise所有的方法,继承给deferred对象。
函数执行以后。导致的结果,就是deferred比promise多出了三个方法。[resolve | reject | notify];
例子(11)
function A() {
var dfb = $.Deferred();
setTimeout(function () {
alert(111);
dfb.resolve();
}, 1000);
return dfb;
}
var df = A();
df.done(function () {
alert('成功 aaa');
}).fail(function () {
alert('fail bbb');
});
df.reject();
// 执行结果,先执行失败,fail bbb
// 在执行111
// 原因是,因为 df.reject(); 先执行,而后,直接就执行了fail, 1秒钟以后,才执行了计时函数,
那如何让外面不能修改呢?
很简单,promise。(这个属性已经在ES6中有独立出来了,就是解决回调问题的。)
function A() {
var dfb = $.Deferred();
setTimeout(function () {
alert(111);
dfb.resolve();
}, 1000);
return dfb.promise(); //这里加上了promise,就变成promise对象。
// 注意啊,promise 是没有 [resolve | reject | notify] 这三个方法的,
// 所以,根本无法改变状态。
// 没有参数,就返回promise对象了。
}
var df = A();
df.done(function () {
alert('成功 aaa');
}).fail(function () {
alert('fail bbb');
});
df.reject(); //这里报错,找不到这个函数。肯定找不到啊。因为promise没有这个函数。
// 执行结果,这回就是返回成功和111.并且,reject还会报错。
// 因为用了promise之后,就不能再进行修改状态了。
例子(12)
查看状态:obj.state();
并传入参数
function A() {
var df = $.Deferred();
alert (df.state()); //pending
setTimeout(function () {
alert(111);
df.resolve('hg'); //传入参数
}, 1000);
return df.promise(); //这里加上了promise
}
var df = A();
df.done(function () {
alert('成功 aaa');
alert(arguments[0]); //可以获取传入的参数
alert (df.state()); //resolve
}).fail(function () {
alert('fail bbb');
alert (df.state());
});
源码:
// Handle state
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 );
}
例子(13)
var df = $.Deferred();
setTimeout(function () {
df.resolve('hg');
}, 1000);
var newDf = df.pipe(function () {
return arguments[0] + " wj";
});
newDf.done(function () {
alert(arguments[0]);
});
// 打印出 hg wj
// pipe的返回值,作为新的状态转移的参数。返回一个新的不能修改状态的延迟对象。
// pipe不影响状态,只是影响参数。
总结一下:
promise。缺少三种状态,只要延迟对象调用promise()方法,就是不能修改状态了。
promise {
//方法。
state : 状态。
有state()的方法,获取状态
always : 总是触发,不管是你完成,还是未完成,都是触发。
always: function() {
deferred.done( arguments ).fail( arguments );
return this;
},
then : /* fnDone, fnFail, fnProgress */ 分开写。
obj,then(function () {
//fnDone
}, function () {
//fnFail
},function () {
//fnProgress
});
promise //如果有参数,就将promise下面的所有方法,都继承给obj对象。
promise: function( obj ) {
return obj != null ? jQuery.extend( obj, promise ) : promise;
}
pipe : 管道,给参数做个处理。并不影响状态转移。
可以理解为继承的延迟对象。
// Keep pipe for back-compat
promise.pipe = promise.then;
done | fail | progress;
}
deferred {
//方法
resolve | reject | notify + promise对象内的属性和方法。
}