解读JSDeferred源码
JSDeferred是日本javascript高手cho45受MochiKit.Async.Deferred模块启发而设计的一个异步执行类库,为javascript实现了java式的next与wait的操作。
入门,创建一个Deferred类
Deferred.define();
next(function(){
/* 处理 */
})
上面会将全局对象变成Deferred的子类,那么它就可以创建Deferred实例。虽然这样很方便,但无疑污染了全局作用域,因此JSDeferred也提供了无侵入的写法:
var o = {}; //定义一个对象
Deferred.define(o);//把Deferred的方法加持到它上面,让o成为一个Deferred子类。
for(var x in o)alert(x);
// parallel
// wait
// next
// call
// loop
o.next(function(){
/* 处理 */
})
或者直接使用Deferred:
Deferred.next(function(){//在暗地里创建一个Deferred实例,然后我们直接"链"下去就行了。
/* 处理 */
})
无论哪一个,最初的next都是一个静态方法,目的是用于提供了一个JSDeferred实例与实现第一个异步操作。异步操作在JSDeferred中有许多实现方法,如setTimeout,img.onerror或 script.onreadystatechange ,它会视浏览器选择最快的异步方式。现在我们看最简单的实现方式,setTimeout。它在next_default静态方法中实现。
Deferred.next_default = function (fun) {
var d = new Deferred();
var id = setTimeout(function () { clearTimeout(id); d.call() }, 0);
d.canceller = function () { try { clearTimeout(id) } catch (e) {} };
if (fun) d.callback.ok = fun;
return d;
};
无论何种next静态方法,最终都会返回一个Deferred实例,因此第二个next方法与第一个next是完全不同。它是一个实例方法,并确定其_post的第一个参数为"ok",并在_post中方法重置callback.ok与设置其_next,从而构造一个如Deferred链
构造Deferred链。
首先,我先提供一个用于讲解的例子吧:
Deferred.define();
next(function func1(){
alert(1)
})
.next(function func2(){
alert(2)
});
alert(3)
与贴出源码开头的部分:
function Deferred () { return (this instanceof Deferred) ? this.init() : new Deferred() }
Deferred.ok = function (x) { return x };
Deferred.ng = function (x) { throw x };
Deferred.prototype = {
init : function () {
this._next = null;
this.callback = {
ok: Deferred.ok,
ng: Deferred.ng
};
return this;
},
next : function (fun) { return this._post("ok", fun) },
error : function (fun) { return this._post("ng", fun) },
call : function (val) { return this._fire("ok", val) },
fail : function (err) { return this._fire("ng", err) },
cancel : function () {
(this.canceller || function () {})();
return this.init();
},
_post : function (okng, fun) {
this._next = new Deferred();
this._next.callback[okng] = fun;
return this._next;
},
_fire : function (okng, value) {
var next = "ok";
try {
value = this.callback[okng].call(this, value);
} catch (e) {
next = "ng";
value = e;
}
if (value instanceof Deferred) {
value._next = this._next;
} else {
if (this._next) this._next._fire(next, value);
}
return this;
}
};
我们整理一下思路:
- 首先,最初的next会创建一个新的Deferred实例(下简称为d1),然后将func1放入d1.callback.ok中。
- 接着,第二个的next会创建一个新的Deferred实例(下简称为d2),然后将func2放入d2.callback.ok中。
- 然后,在_post中将d1._next设置为d2。
- 最后,setTimeout 0ms会把它里面注册的函数放到当前待执行函数的后面,因此会先执行alert(3),然后再function () { clearTimeout(id); d.call() },再即执行d1.call()。call方法再调用_fire方法,从而执行d1.callback.ok。如果d1.callback.ok的返回值不为Deferred实例,并且d1._next不为空,则执行d1._next.callback.ok,相当于执行d2.callback.ok。
Deferred链的实现。
在上面最后一步我们也提到了,Deferred链是否能延续取决了两个东西,当前Deferred实例在_fire中的执行callback的返回值与其_next。_next的设置目前我们看到有两个地方,_post实例方法与_fire实例方法。当我们执行到第二个next实例方法时,就会调用_post方法。在想调用_fire方法,我们目前只是通过call方法达到,而call位于setTimeout中。由于setTimeout的零秒延迟,换言之,当我们执行call方法时,程序已经为d1准备好callback与_next了,然后再进入_fire方法中:
要注意一下Deferred.prototype._fire内的this实质为d1。
value = this.callback[okng].call(this, value);
//相当于d1.callback.ok.call(d1,null)
//相当于func1.call(d1,null)
//相当于d1.func1();
如果func1正常执行,并且返回值不为Deferred的实例,由于它已经拥有_next,因此接着便是再一次执行_fire方法,这次它的调用者为this._next,即d2:
if (this._next) this._next._fire(next, value);
//相当于if(d2)d2.fire(next,value)
//相当于if(d2)d2.fire("ok",null)
//相当于d2.call()
由于d2的_next为null,func2也没有返回值,程序就此结束。最后提一下,func1与func2是同步执行的,因为它们之间没有用setTimeout隔开。
下回预告:
如果func1与func2异步执行,可以这样写:
next(function func1(){
/* 处理 */
})
.wait(0)
.next(function func2(){
/* 处理 */
})
或者:
next(function func1(){
alert("1")
throw 'error'
})
.next(function func2(){
alert("2")
})
.error(function func3(){
alert("3")
})
这时它执行func1后会跳过func2,执行func3!
浙公网安备 33010602011771号