好像有这么一句名言——"每一个优雅的接口,背后都有一个龌龊的实现"。最明显的例子,jQuery。之所以弄得这么复杂,因为它本来就是那复杂。 虽然有些实现相对简明些,那是它们的兼容程度去不了那个地步。当然,世上总有例外,比如mootools,但暴露到我们眼前的接口,又不知到底是那个父类 的东西,结构清晰但不明撩。我之所以说这样的话,因为异步列队真的很复杂,但我会尽可能让API简单易用。无new实例化,不区分实例与类方法,链式,等 时髦的东西都用上。下面先奉上源码:
;(
function
(){
|
var
dom =
this
.dom =
this
.dom || {
|
mix :
function
(target, source ,override) {
|
var
i, ride = (override === void 0) || override;
|
for
(i
in
source) {
|
if
(ride || !(i
in
target)) {
|
target[i] = source[i];
|
}
|
}
|
return
target;
|
}
|
}
|
//////////////////////////////////////////////////////////////////////
|
//=======================异步列队模块===================================
|
var
Deferred = dom.Deferred =
function
(fn) {
|
return
this
instanceof
Deferred ?
this
.init(fn) :
new
Deferred(fn)
|
}
|
var
A_slice = Array.prototype.slice;
|
dom.mix(Deferred, {
|
get:
function
(obj){
//确保this为Deferred实例
|
return
obj
instanceof
Deferred ? obj :
new
Deferred
|
},
|
ok :
function
(r) {
//传递器
|
return
r
|
},
|
ng :
function
(e) {
//传递器
|
throw
e
|
}
|
});
|
Deferred.prototype = {
|
init:
function
(fn){
//初始化,建立两个列队
|
this
._firing = [];
|
this
._fired = [];
|
if
(
typeof
fn ===
"function"
)
|
return
this
.then(fn)
|
return
this
;
|
},
|
_add:
function
(okng,fn){
|
var
obj = {
|
ok:Deferred.ok,
|
ng:Deferred.ng,
|
arr:[]
|
}
|
if
(
typeof
fn ===
"function"
)
|
obj[okng] = fn;
|
this
._firing.push(obj);
|
return
this
;
|
},
|
then:
function
(fn){
//_add的包装方法1,用于添加正向回调
|
return
Deferred.get(
this
)._add(
"ok"
,fn)
|
},
|
once:
function
(fn){
//_add的包装方法2,用于添加负向回调
|
return
Deferred.get(
this
)._add(
"ng"
,fn)
|
},
|
wait:
function
(timeout){
|
var
self = Deferred.get(
this
);
|
self._firing.push(~~timeout)
|
return
self
|
},
|
_fire:
function
(okng,args,result){
|
var
type =
"ok"
,
|
obj =
this
._firing.shift();
|
if
(obj){
|
this
._fired.push(obj);
//把执行过的回调函数包,从一个列队倒入另一个列队
|
var
self =
this
;
|
if
(
typeof
obj ===
"number"
){
//如果是延时操作
|
var
timeoutID = setTimeout(
function
(){
|
self._fire(okng,self.before(args,result))
|
},obj)
|
this
.onabort =
function
(){
|
clearTimeout(timeoutID );
|
}
|
}
else
if
(obj.arr.length){
//如果是并行操作
|
var
i = 0, d;
|
while
(d = obj.arr[i++]){
|
d.fire(args)
|
}
|
}
else
{
//如果是串行操作
|
try
{
//
|
result = obj[okng].apply(
this
,args);
|
}
catch
(e){
|
type =
"ng"
;
|
result = e;
|
}
|
this
._fire(type,
this
.before(args,result))
|
}
|
}
else
{
//队列执行完毕,还原
|
(
this
.after || Deferred.ok)(result);
|
this
._firing =
this
._fired;
|
this
._fired = [];
|
}
|
return
this
;
|
},
|
fire:
function
(){
//执行正向列队
|
return
this
._fire(
"ok"
,
this
.before(arguments));
|
},
|
error:
function
(){
//执行负向列队
|
return
this
._fire(
"ng"
,
this
.before(arguments));
|
},
|
abort:
function
(){
//中止列队
|
(
this
.onabort || Deferred.ok)();
|
return
this
;
|
},
|
//每次执行用户回调函数前都执行此函数,返回一个数组
|
before:
function
(args,result){
|
return
result ? result
instanceof
Array ? result : [result] : A_slice.call(args)
|
},
|
//并行操作,并把所有的子线程的结果作为主线程的下一个操作的参数
|
paiallel :
function
(fns) {
|
var
self = Deferred.get(
this
),
|
obj = {
|
ok:Deferred.ok,
|
ng:Deferred.ng,
|
arr:[]
|
},
|
count = 0,
|
values = {}
|
for
(
var
key
in
fns){
//参数可以是一个对象或数组
|
if
(fns.hasOwnProperty(key)){
|
(
function
(key,fn){
|
if
(
typeof
fn ==
"function"
)
|
fn = Deferred(fn);
|
fn.then(
function
(value){
|
values[key] = value;
|
if
(--count <= 0){
|
if
(fns
instanceof
Array){
//如果是数组强制转换为数组
|
values.length = fns.length;
|
values = A_slice.call(values)
|
}
|
self._fire(
"ok"
,[values])
|
}
|
}).once(
function
(e){
|
self._fire(
"ng"
,[e])
|
});
|
obj.arr.push(fn);
|
count++
|
})(key,fns[key])
|
}
|
}
|
self.onabort =
function
(){
|
var
i = 0, d;
|
while
(d = obj.arr[i++]){
|
d.abort();
|
}
|
}
|
self._firing.push(obj);
|
return
self
|
},
|
//处理相近的迭代操作
|
loop :
function
(obj, fn, result) {
|
obj = {
|
begin : obj.begin || 0,
|
end : (
typeof
obj.end ==
"number"
) ? obj.end : obj - 1,
|
step : obj.step || 1,
|
last :
false
,
|
prev :
null
|
}
|
var
step = obj.step,
|
_loop =
function
(i,obj){
|
if
(i <= obj.end) {
|
if
((i + step) > obj.end) {
|
obj.last =
true
;
|
obj.step = obj.end - i + 1;
|
}
|
obj.prev = result;
|
result = fn.call(obj,i);
|
Deferred.get(result).then(_loop).fire(i+step,obj);
|
}
else
{
|
return
result;
|
}
|
}
|
return
(obj.begin <= obj.end) ? Deferred.get(
this
).then(_loop).fire(obj.begin,obj) :
null
;
|
}
|
}
|
//将原型方法转换为类方法
|
"loop wait then once paiallel"
.replace(/\w+/g,
function
(method){
|
Deferred[method] = Deferred.prototype[method]
|
});
|
})();
|
Deferred提供的接口其实不算多,then once loop wait paialle就这五个,我们可以new一个实例出来,用它的实例方法,可以直接用类名加方法名,其实里面还是new了一个实例。另外,还有两个专门用于 复写的方法,before与after。before执行于每个回调函数之前,after执行于所有回调之后,相当于complete了。既然是列队,就 有入队操作与出队操作,我不可能使用queue与dequeue这样土的命名。queue换成两个时间状语,then与once,相当于jQuery的 done、fail,或dojo的addCallback、addErrback。dequeue则用fire与error取替,jQuery1.5的 beta版也曾经用过fire,不知为何换成resolve这样难记的单词。好了,我们先不管其他API,现在就试试身手吧。
var
log =
function
(s) {
|
window.console && window.console.log(s);
|
}
|
dom.Deferred(
function
() {
|
log(1);
//1
|
})
|
.then(
function
() {
|
log(2);
//2
|
})
|
.then(
function
() {
|
log(3);
//3
|
})
|
.fire();
|
//如果不使用异步列队,实现这种效果,就需要用套嵌函数
|
/*
|
var fun = function(fn){
|
fn()
|
};
|
fun(function(){
|
log(1);
|
fun(function(){
|
log(2);
|
fun(function(){
|
log(3);
|
})
|
});
|
});
|
*/
|
当然,现在是同步操作。注意,第一个回调函数是作为构造器的参数传入,可以节约了一个then^_^。
默认如果回调函数存在返回值,它会把它作为下一个回调函数的参数传入,如:
dom.Deferred(
function
(a) {
|
a += 10
|
log(a);
//11
|
return
a
|
})
|
.then(
function
(b) {
|
b += 12
|
log(b);
//23
|
return
b
|
})
|
.then(
function
(c) {
|
c += 130
|
log(c);
//153
|
})
|
.fire(1);
|
我们可以重载before函数,让它的结果不影响下一个回调函数。在多投事件中,我们也可以在before中定义,如果返回false,就中断队列了。
我们再来看它如何处理异常。dom.Deferred的负向列队与jQuery的是完全不同的,jQuery的只是正向列队的一个克隆,而在dom.Deferred中,负向列队只是用于突发情况,是配角。
dom.Deferred(
function
() {
|
log(1111111111)
//11111111111
|
}).
|
then(
function
() {
|
throw
"error!"
;
//发生错误
|
}).
|
then(
function
(e) {
//这个回调函数不执行
|
log(e+
"222222"
)
|
return
2222222
|
}).
|
once(
function
(e){
//直到 遇上我们自定义的负向回调
|
log(e+
'333333'
)
//error!333333
|