上一课主要分析了jQuery1.51版本的jQuery Deferred。在jQuery1.6中,jQuery Deferred添加了两个方法,always,pipe。
always用来添加回调,无论成功还是失败,都会执行。
pipe就是管道的意思,对回调使用瀑布模型,上一个回调的返回值供下一个会调使用。
我们举个例子来说明下pipe与then的区别:
var deferred = $.Deferred(); //返回的是Deferred对象
var promise = deferred.pipe(function(a){return a*2}).pipe(function(a){return a*4;}) //pipe方法的返回值是Promise对象,因此promise与deferred不相等。
deferred.resolve(5); //执行这个函数后,就会调用pipe添加进来的回调函数。第一个回调函数中的a=5,然后返回10给第二个回调函数,因此第二个回调中的a=10。
$.Deferred().then(function(a){return a*2;}).then(function(a){return a*4;}).resolve(5);
第一个回调函数中的a=5,第二个回调函数中的a=10,跟pipe方法一样。它们的区别是then方法返回的是Deferred对象,因而可以进行链式操作,而pipe方法返回的是Promise对象,不能进行链式操作。
在这个版本的jQuery中,Deferred开始在jQuery内部大规模应用了,包括queue模块,Ajax模块。
举个很好的例子:
var stuff1 = function(deferred){
setTimeout(function(){deferred.resolve()},1000);
};
var stuff2 = function(deferred){
setTimeout(function(){deferred.resolve()},500);
};
var stuff3 = function(deferred){
setTimeout(function(){deferred.resolve()},800);
};
var stuff4 = function(deferred){
setTimeout(function(){deferred.resolve()},800);
};
$.when( //此方法接受了三个Deferred对象,只有等这三个Deferred对象的状态结束后(也就是执行了各自的resolve方法后),才会执行then添加的回调方法。
$.Deferred(stuff1), //此方法会执行stuff1方法,并且把新创建的Deferred对象传进去,返回此Deferred对象,1秒后,调用此Deferred对象的resolve方法。
$.Deferred(stuff2), //同上,只是异步处理的时间不一样。
$.Deferred(stuff3) //同上,这里的setTimeout的异步处理,我们可以当做是ajax请求的时间长度。
).then(stuff4); //给$.when()方法返回的Deferred对象添加回调函数stuff4。等when方法中传入的所有Deferred对象的状态结束后,就会执行此回调stuff4.
这段代码的理解你可以当做是:有三个ajax请求,请求的地址不一样,我们需要等这三个ajax请求都返回后,把这三个请求回来的数据进行整合处理,才能进行最后一个ajax请求。
jQuery1.7添加了Callbacks模块,具体可以看:http://www.cnblogs.com/chaojidan/p/4165818.html。
而且也改写了Deferred模块。这个时候的Deferred,它是一个三链结构(doneList,failList,progressList)。progressList链添加了三个API,progress,notify,notifyWith,用来给此链添加回调和触发回调。progressList链是用来干嘛的?我们知道成功队列doneList与失败队列failList都是一次性的,也就是说触发了一次就不能再触发了。但是有时,我们需要反复触发,那么就可以用progressList来解决。
举个例子:
var a = $.Deferred();
a.done(function(x){console.log(x);}).done(function(x){console.log(x)});
a.resolve(1);
如果大家看了上一课的jQuery Deferred的源码,就知道,创建了一个新的延迟对象a,然后通过done方法添加了两个成功回调函数,最后通过resolve方法触发a的成功回调,因此会执行这两个成功回调函数,打印出两个1.
这时如果你再调用a.resolve(2);再次触发a的成功回调,不会打印出两个1.
但是progressList可以,
var b = $.Deferred();
b.progress(function(x){console.log(x);}).progress(function(x){console.log(x)});
b.notify(1);
b.notify(2);
第一个notify会打印出两个1,第二个notify会打印出两个2.
从上面的例子中,大家可以看出,doneList,failList触发一次状态后,就会结束了,你后面再次触发同样的状态,是不会执行回调函数的。但是progressList可以触发多次状态,也就意味着你只要调用notify,就会执行progress添加的回调函数。
jQuery1.8又对Deferred进行了重构,不过思路是一致的。
Promise/A隶属于Promise规范,而Promise规范则又隶属于CommonJS。
Promise/A规范大概是这样描述的:一个带有then方法的对象,它拥有3个状态,fulfilled,rejected,pending。then方法可以传入三个函数,一个是成功时执行的回调函数onFulfill,一个是失败时执行的回调函数onReject,一个是进行中执行的回调函数onNotify,它不会改变对象的状态(刚开始对象就处于pending,当执行成功回调函数onFulfill后,对象就会变成fulfilled状态,当执行失败回调函数onReject后,对象就会变成rejected状态,当执行进行中回调函数onNotify后,对象状态不会改变,还是pending状态)。这三个函数都是可选的,如果是非函数,就忽略掉。then方法会返回一个新的Promise对象,以便能够实现链式操作。
后来人们在Promise/A的规范上添加了更多的细节,形成了Promise/A+规范。现在市面上有三大Promise/A+库,分别是:Q,RSVP,when。其中,Q的微缩版被整进angular.js,RSVP被整进了ember.js。这两个库就是著名的MVVM库。后面的章节将会讲到这两个库,我也很期待。
js异步处理的前景
未来的javascript将支持yield生成器(generator),目前只有Firefox浏览器有,但是需要自己打开实验性开关。
<script type="text/javascript; version=1.7"> //这里改成1.8也行
//这里面就可以使用yield了
</script>
此yield,可以让我们轻松的以同步的形式写出异步的代码。你只需要将它们放到某个函数体内。相应的库有:taskjs,gens,tamejs等。
有人对各种不同实现的异步库进行性能评测,基于原生生成器的库总体上占优势。yield语句比回调更能及时的工作,耗时更少。
但是在目前,实现生成器的代码量非常大,而实现Promise则相对轻松一些,因此Promise是当下最廉价的异步方案。
yield还没有真正的发展起来,因此不用精读。知道有这么一个东西就行。
下一课,将讲解Ajax,大家可以先去看一下Ajax Hacks这本书。
加油!