最近准備寫一個系列的文章來談jQuery的種種技巧和原理。
今天講Deferred風格的Ajax,本篇分爲兩部分,第一部分介紹Deferred風格的Ajax的基本使用,第二部分深入一點介紹原理。
基本使用
Deferred是jQuery1.5加入的特性,可以把延遲函數寫成鏈式的樣子,開發更加方便和靈活些。
先來看一個例子:
1 |
$.ajax( '/test.html' ).done( function (html) { |
這是deferred在Ajax中的典型應用,這段代碼的意思是說請求一個Ajax請求,待請求完成的時候執行一個函數,將結果打印出來。
在1.5以前,我們是這樣實現的:
1 |
$.ajax({url: '/test.html' , success: function (html) { |
下面我們給一個對照表
:
參數 |
Deferred函數 |
觸發事件 |
success |
success/done |
請求成功 |
error |
error/fail |
請求失敗 |
complete |
complete/always |
請求成功&失敗 |
深入解析
Deferred object
要說清楚這裡面的原理,我們不得不窺探一下$.Deferred object了。
先來看一個簡單的例子:
5 |
console.log( '1 - ' + d.state()); |
7 |
console.log( '2 - ' + d.state()); |
執行結果:
1 - pending
Done
2 - resolved
從這裡我們可以看到以下幾點:
- $.Deferred object是有狀態的
- 注冊一個done函數之後,並不會立即被執行
- relove可以修改d的狀態
- 狀態發生變化的時候會調用之前注册的done函數
我們再看一例:
03 |
console.log( 'Failed' ); |
06 |
console.log( 'Completed' ); |
08 |
console.log( '1 - ' + d.state()); |
10 |
console.log( '2 - ' + d.state()); |
12 |
console.log( '3 - ' + d.state()); |
執行結果:
1 - pending
Failed
Completed
2 - rejected
3 - rejected
always就是不管成功還是失敗總是執行,而且在done/fail函數之後執行。另外我們先把狀態改成了rejected,再調用resolve就無效了。
我們調整一下調用順序看看會怎樣:
2 |
console.log( '1 - ' + d.state()); |
4 |
console.log( '2 - ' + d.state()); |
執行結果:
1 - pending
2 - rejected
Failed
我們先改狀態,後注冊fail函數,fail函數還是執行了
再把always加進去看看會發生什麼
03 |
console.log( 'Completed' ); |
05 |
console.log( '1 - ' + d.state()); |
07 |
console.log( '2 - ' + d.state()); |
09 |
console.log( 'Failed' ); |
執行結果:
1 - pending
Completed
2 - rejected
Failed
沒有done & fail的時候,always就不會傻等了,自己直接先執行了。
總結幾點好了:
- 内部維護一個狀態,外部可以通過reslove/reject來改變之。
- 狀態發生變化的時候會調用相應的方法,當然如果没有就算了。
- 給 object注册方法的時候,如果狀態已經變化至此,會立即執行的。
- 狀態一旦發生變化就不能再改變了,就是說不能這樣pedding->resolved->rejected
好了,了了這些$.Deferred的相關内容,我們再來看看實際的場合會怎樣用這個咚咚吧。
01 |
function longTimeJob() { |
02 |
var deferred = $.Deferred(); |
04 |
setTimeout( function () { |
06 |
if ( new Date().getTime() % 2) { |
07 |
console.log( "My job done" ); |
10 |
console.log( "My job failed" ); |
17 |
longTimeJob().done( function () { |
20 |
console.log( 'Failed' ); |
21 |
}).always( function () { |
22 |
console.log( 'Completed' ); |
執行結果:
My job failed
Failed
Completed
longTimeJob是一個需要3秒以上才能完成的任務,但函數會立即返回,狀態的改變是異步的,感覺像是多線程的,所以我們可以調用完之後立即注册done/fail/always函數,這樣當job的狀態發生變化的時候就會調用我們此前注册的函數。
看到這裡再回想一下文章最開始處給出的Ajax的例子,是不是有幾分相似呢?其實呢,原理是一樣的啦。ajax同樣是一個耗時的工作,但會立即返回(除非你指定了async爲false)。
其實呢,ajax的返回值和我們這裡的longTimeJob還是有區别的,而是一個Promise和XMLHTTPRequest綜合體。Promise可以理解爲是一個只讀版的$.Deferred object,没有reject/resolve,其他都一樣。同時該 object還有一些操作XMLHTTPRequest object的功能,比如getResponseHeader什麽的,請看下圖。
獲得Promise object的方法很簡單,$.Deferred().promise()就可以了。
完。