異步方式的概述
通過事件達到異步操作
通過我們最熟悉的回調
類promise的方式
首先我們注意到1.5版本前后的jquery有一個重要的不同
//before 1.5
var option={
type:'GET',
url:'...',
success:function(){..},
fail:function(){..},
complete:function(){..}
};
$.ajax(option);
//after 1.5
$.ajax(option)
.done(function(){..})
.fail(function(){..})
.progress(function(){..})
.always(function(){..})
1.5版本之后變成了優美的鏈式調用,並且可以對同一事件增加多個回調函數,原因是原生的xhr對象換成了jqxhr對象,里面有什么魔法呢?
從promise講起
promise在javascript編程世界里可以說是大名鼎鼎,下面這段代碼給出了它的簡單用法。
promise(function(){..}).
then([successhandler1,successhandler2,..],
[failhandler1,failhandler2,...]);
promise為我們呈現出了異步編程的一種新模式,但它還不夠漂亮,接下來我們看一看jQuery引入的deferred。
jQuery的Deferred
創建Deferred對象
var de=$.Deferred();//空的Deferred對象
$.Deferred(function(){..}).
done(function(){..});
//會直接執行里面的function並返回一個Deferred對象
function tmp(defer){
....
return defer;
};//這個寫法的含義我們留在后面再講
Deferred的機理
我們可以這樣想,每個deferred對象內部有一個隱藏着的狀態變量,有成功、失敗、執行中三種狀態,當程序執行成功則會把它設為成功,否則設為失敗。這里的成功失敗指的是滿足某種設定條件,並非一般意義上的出錯,即使拋出異常也不認為其失敗。我們可以在程序的運行過程中根據檢測到的狀態來決定之后哪些回調函數會被執行。
設定狀態實例
$(function(){
....
if(..) this.resolve(arg1,arg2,..);
//會執行done和always回調,args是傳入回調的參數
//同類函數this.resolveWith(content,[args..]);content作為回調函數中的this
else this.reject(arg1,arg2,..);
//執行fail和always回調,rejectWith基本同上
//需要注意的是always也必須狀態改變才能調用,若一直處於執行狀態,也不會調用
....
this.notify(args);//調用progress回調,但必須在狀態變為成功或失敗前發起,這里progress不會被調用
//notifyWith基本同上
}).fail(..).done(..).progress(..);
還需要注意的是,即使狀態發生改變了,程序還會繼續運行。這里所說的成功失敗指的是滿足我們設定的條件與否,和程序真的運行狀態無關,done、fail、always返回的依然是原本的deferred對象。
請記住無論什么回調都是在函數執行完后調用,函數執行不會被中斷
最后還要注意一點未防止狀態在程序外部被更改,應該加上一句
$.Deferred(function(){..}).promise().done(..).fail(..);
但promise返回的不再是原本的Deferred對象了,也不能在外部更改其狀態了。
強勁的when
看到這里我們可能有一個疑問,如果我們有一系列任務來決定后來的回調怎么辦,不用擔心我們有when。
$.when(defer1,defer2...);
//這時候如果每個defer還要$(func(){..})就很難寫了,所以就回到我們開始介紹的最后一種,利用函數返回
var defer1=$.Deferred();
var defer2=$.Deferred();
function func1(defer){...return defer;}
function func2(defer){...return defer;}
$.when(func1(defer1),func2(defer2)).done(..).fail(..);
//當全部defer成功done才會被調用,有一個defer失敗fail就會調用,但無論怎樣一個回調函數最多被調用一次,且不會影響所有函數的執行
var tmp=9;
$.when(tmp).done(..).fail(..);
//當when鏈里面全部不是defer變量時會直接執行done回調,注意是全部,有defer變量會跳過非defer,回調取決於defer