【JQuery】$.when、$.Deferred

前言

前端網站初始化常需要透過一個或多個$.Ajax先跟後端要取資料,
你可以想像$.Ajax是送出去的一隻鴿子
每一隻鴿子都有自己的目標地,腳下裝著要帶給後端的訊息,
這些鴿子訪問完目標地後,會把你需要的資料帶回來,讓你顯示在前端網頁上,

你不太確定這些鴿子什麼時候回來
你也不確定這些鴿子是否會帶回正確的資料(假如後端程式出錯),
你網站初始化時,不太可能等這些鴿子都回來再繼續下一個步驟,
 

$.when配合$.Deferred()

透過JQuery的$.when配合$.Deferred()完成多個$.ajax的異步處理,
當鴿子送出去後,你可以先執行其他的程式碼,
當鴿子回來後,再利用鴿子帶回的資料執行部分程式碼,
控制這群鴿子回來時,如果資料正確則做什麼、如果產生錯誤則做什麼,及不管如何皆執行什麼,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
$.when(function1(), function2()).done(function(result1, result2) {
console.log('success code');
}).fail(function () {
console.log('error code');
}).always(function () {
console.log('always do');
});

function function1() {
var dfd = $.Deferred();
$.ajax({
url : '',
type: 'GET',
date: {},
}).done(function(res) {
dfd.resolve(res);
}).fail(function() {
dfd.reject();
});
return dfd.promise();

function function2() {
var dfd = $.Deferred();
$.ajax({
url : '',
type: 'GET',
date: {},
}).done(function(res) {
dfd.resolve(res);
}).fail(function() {
dfd.reject();
});
return dfd.promise();
};

 

$.Deferred()控制返回

這裡可以看到$.ajax常使用$.Deferred()來控制返回時的各種狀況,

$.Deferred有一個狀態的概念:

  • 使用$.Deferred.resolve(),將狀態更改為resolved,此時$.when就會執行done裡面的程式碼,
  • 使用$.Deferred.reject(),將狀態更改為rejected,此時$.when就會執行fail裡面的程式碼,
  • 最後JQuery官方建議使用return $.Deferred.promise(),防止其他程式變更這個狀態,結束這個function的呼叫,
     

另外
我們也可以直接宣告一個$.Deferred物件,來控制某段程式執行後,
當你不確定他什麼時候完成,但有一片段,需要等他完成後才來執行的情形,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
var alertFun = function(){
var dfd = $.Deferred(); //新建一個Deferred對象
var task = function(){ //五秒後執行task()
alert('第三個執行');
dfd.resolve('第四個執行'); //改變Deferred執行狀態回到$.when
alert('第六個執行');
};

setTimeout(task, 5000);
alert('第一個執行!');

return dfd.promise();
alert('永遠不會執行!');
};

$.when(alertFun()).done(function(resource){ //呼叫alertFun
alert(resource); //此時resource = '第四個執行'
}).fail(function(){
alert('出錯');
}).always(function () {
alert('第五個執行');
});
alert('第二個執行');

結語

  1. task來模擬那個不確定什麼時候完成的任務,假設需要五秒時間執行,故繼續執行$when之後的alert(‘第二個執行’)。
  2. 五秒後繼續執行task方法,遇到dtd.resolve,代表Deferred對象執行完畢可跳到done內。
  3. done執行的function()參數等於dtd.resolve()放入參數。
  4. return dtd.promise()後面的程式碼不會執行,dtd.resolve()後的程式碼會執行。
  5. 如果拿掉$.Deferred(),他執行完alertFun()裡面的程式,會被視為resolved狀態,不會等待五秒後遇到dfd.resolve()才執行done(),而是立刻執行,但此時你的五秒任務(也許是要資料)尚未完成。