Jquery Deferred 詳解

學會使用Deferred對象以及其相關函數, 可以優雅的解決開發過程中一些異步執行問題

----應用篇----

開發場景

你是否經歷過這種代碼場景:
<code>function demo1(){
.....

const result = demo2();
if(result){
....
}else{
....
}

.....
}

function demo2(){
const changeStatus = true; //一個可預期的返回值
$.ajax({
url: "xxx",
async: true,
.....
.....
success: function (reslut) {
if(.....){
changeStatus = false;
}
}
});
return changeStatus;
}/<code>

上述代碼在執行時, 你會發現changeStatus會一直返回true

原因: ajax為非阻塞代碼塊, 在發送請求時不會阻塞當前線程的執行, 而請求是耗時動作, 必然會比代碼執行速度慢, 所以在success方法執行之前, 方法已經返回changeStatus的值
解決辦法:
  1. 把ajax設置成同步請求, async: false, 帶來的問題: 如果請求耗時時間過長, 會造成頁面假死的情況,
  2. 把後續邏輯都放到success中(抽取受到返回值影響的代碼塊, 放到success中)
<code>function demo1(){

.....

demo2();

.....

}

function demo2(){
const changeStatus = true; //一個可預期的返回值
$.ajax({
url: "xxx",
async: true,
.....
.....
success: function (reslut) {
if(.....){
changeStatus = false;
}
//轉移的代碼, 此代碼塊受到changeStatus的狀態影響
if(changeStatus){
....
}else{
....
}
}
});
return changeStatus;
}
/<code>

此種做法, 增加了代碼的維護成本, 也使代碼可讀性變差(醜)

那麼可不可以用一種優雅的方式, 解決這個問題呢?

----Deferred篇----

名詞解釋

deferred: 延期的, 推遲

對象的功能說明

<code>A Deferred object starts in the pending state. Any callbacks added to the object with 
deferred.then(), deferred.always(), deferred.done(), or deferred.fail() are queued to
be executed later. Calling deferred.resolve() or deferred.resolveWith() transitions the
Deferred into the resolved state and immediately executes any doneCallbacks that are set.
Calling deferred.reject() or deferred.rejectWith() transitions the Deferred into the rejected
state and immediately executes any failCallbacks that are set. Once the object has entered the
resolved or rejected state, it stays in that state. Callbacks can still be added to the resolved
or rejected Deferred — they will execute immediately./<code>

簡單翻譯: deferred對象在初始化的時候是延遲狀態的, 任何添加到隊列裡的回調方法會推遲執行, 調用resolve()或者 resolveWith()方法來轉換當前實例進入到resolve狀態, 並且執行對應狀態的回調方法, 調用reject()或rejectWith()方法 來使實例進入reject狀態, 並執行對應的回調方法, 一個對象實例當進入到不同的狀態時, 它會保持狀態, 回調方法會添加到當前 實例中, 並被立即執行

對象的方法說明

deferred.then(doneFilter, failFilter, progressFilter):

<code>doneFilter
Type: Function()
當實例為resolve狀態時被觸發

failFilter
Type: Function()
當實例為rejected狀態時被觸發

progressFilter
Type: Function()
當實例狀態改變時會被觸發/<code>

deferred.always(alwaysCallbacks)

<code>alwaysCallbacks
Type: Function()
實例狀態改變時總會被觸發, 參數為function或function數組/<code>

deferred.done(doneCallbacks)

<code>doneCallbacks
Type: Function()
實例狀態為resolve時會被觸發, 參數為function或function數組/<code>

deferred.fail(failCallbacks)

<code>failCallbacks
Type: Function()
實例狀態為 rejected時會被觸發, 參數為function或function數組/<code>

應用場景

jquery提供兩種方式返回deferred對象: $.ajax $.when

  1. $.ajax().then().fail()...
  2. $.when(...).done().fail()...

著重講一下when, 這個方法在不傳參的時候, 會返回一個resolve狀態的promise(不可變狀態的deferred對象)

when可接收多個deferred對象實例, 用來判斷最終的狀態, exp:

<code>const d1 = $.Deferred();
const d2 = $.Deferred();

$.when( d1, d2 ).done(function ( v1, v2 ) {
console.log( v1 ); // "Fish"
console.log( v2 ); // "Pizza"
});
// 只有所有deferred對象為resolve狀態, 回調函數done才會執行
d1.resolve( "Fish" );
d2.resolve( "Pizza" );/<code>

----代碼改進篇----

通過以上學習, 我們可以知道, 使用deferred對象和when配合, 可以達到方法延期執行的效果

so, 改變原來的代碼如下:

<code>
const ajaxDone = $.Deferred();

function demo1(){
.....

demo2();

$.when(ajaxDone).done(function(result) {
if(result){
....
}else{
....
}
});

.....
}



function demo2(){
$.ajax({
url: "xxx",
async: true,
.....
.....
success: function (reslut) {
if(.....){
ajaxDone.resolve(false);
}
ajaxDone.resolve(true);
}
});
}/<code>

關於Deferred的應用場景, 還有很多, 在開發過程中, 一定要靈活運用, 多思考, 花心思來打磨自己的代碼, 才會使自己的技術能力得到提高

個人博客 點擊前往


分享到:


相關文章: