jQuery.deferred()詳解

/** 請注意 這個代碼是結合 阮老師 的博客來實現的.
 * 網址 http://www.ruanyifeng.com/blog/2011/08/a_detailed_explanation_of_jquery_deferred_object.html
 * 如果有不懂的請看 阮老師 的網址
 */

// JQ 1.5 版本之前的 AJAX 方法
/**
 * success : 這是一個成功的回調函數
 * error : 這是一個失敗的回調函數
 */
$.ajax({
    type: "get",
    url: "./test1.json",
    data: "",
    dataType: "json",
    success: function(response) {
        console.log(response);
    },
    error: function(err) {
        console.log(err);
    }
});

// JQ 1.5 版本之後的 AJAX 方法
/**
 * done : 這是一個成功的回調函數
 * fail : 這是一個失敗的回調函數
 */
// (寫法1)
$.ajax('./test1.json').done(function() {
    console.log('成功了 ');
}).fail(function() {
    console.log('失敗了');
})

/**
 * then : then 接受兩個參數, 一個是成功的回調函數, 一個是失敗的回調函數
 *        如果成功了,會調用第一個回調函數, 如果失敗了, 會調用第二個回調函數
 */
// (寫法2)
$.ajax('./test1.json').then(function() {
    console.log('成功了');
}, function() {
    console.log('失敗了');
})

/** 
 * ------------------------------------------------ $.when() ------------------------------------------------------
 * (引入 $.when() )
 * 提供一種方法來執行零個或多個( Thenable \ then \ Deferred ) 對象的回調函數,通常是表示異步事件
 * 如果沒有參數傳遞給 jQuery.when(),它會返回一個resolved狀態的Promise。
 * 切記 : 如果傳參數 那麼 他將永遠返回的是 成功 的狀態, 失敗定義的回調函數, 永遠不會被執行, 因爲不會遭到拒絕
 * 如果傳參是 不是一個 Deferred 或 Promise 對象, 那麼他將永遠返回的是 成功 的狀態, 失敗定義的回調函數, 永遠不會被執行, 因爲不會遭到拒絕
 * 
 * 上面說的兩種 都不是 異步對象, 所以會直接返回 成功時的回調
 * 如果說是成功的回調, 我們需要調用方法, done 和 fail 只是定義方法, 並沒有調用, 不調用 並不會執行!!!
 * resolve() : 這是調用成功的回調, 也就是說 定義的 done 的回調
 * reject() : 這是調用失敗的回調, 也就是說 定義的 fail 的回調
 * 如果定義了 兩個回調, 並且也執行了 resolve 和 reject, 只會執行最先執行的方法.
 * 如果定義了 成功的 回調, 卻執行了 reject 方法, 那麼並不會執行,  因爲你沒有定義方法
 */

$.when().done(function() {
    console.log('成功了'); /** 成功了 */
}).fail(function() {
    console.log('失敗了');
})

$.when({ testing: 123 }).done(
    function(x) { alert(x.testing); } /* alerts "123" */
);

var dtd = $.Deferred()
$.when(dtd).done(function() {
    console.log('1111');
}).fail(function() {
    console.log('2222');
})
dtd.resolve() // 111
dtd.reject() // 不會執行,因爲並沒有定義失敗的回調

/**
 * $.when(參數1, 參數2, 參數3...)
 * 此方法接受多個參數, 但是返回的結果卻是不一樣的, 但是無非還是兩種狀態, 一種成功時的狀態, 一種失敗時的狀態
 * 成功時的狀態 : 必須所有的對象都返回成功, 纔會返回成功的回調函數
 * 失敗時的狀態 : 如果其中只要有一個返回失敗, 都會返回失敗的回調函數
 * 如果定義函數的時候給了參數, 調用函數的時候沒給參數, 那麼值將會是 undefined
 */

/** d1, d2 時定義了兩個 異步 對象 */
var d1 = $.Deferred();
var d2 = $.Deferred();
/** 調用 when 方法, 並且設置 ( 此處只是設置或者說定義, 並不是調用 ) 成功的回調*/
$.when().done(function(v1, v2) {
    console.log(v1); // "Fish"
    console.log(v2); // "Pizza"
});
/** 調用成功時的方法 */
d1.resolve("Fish");
d2.resolve("Pizza");

/** 傳入多個參數的方法 */
var d1 = $.Deferred();
var d2 = $.Deferred();
var d3 = $.Deferred();
var d4 = $.Deferred();
var d5 = $.Deferred();
/** 調用 when 方法, 並且設置 ( 此處只是設置或者說定義, 並不是調用 ) 成功的回調*/
$.when(d1, d2, d3, d4, d5).done(function(v1, v2, v3, v4, v5) {
    console.log(v1); // "Fish"
    console.log(v2); // "Pizza"
    console.log(v3); // undefined
    console.log(v4); // is Array  [1, 2, 3]
    console.log(v5); // is Object {a: "a"}
});
/** 調用成功時的方法 */
d1.resolve("Fish");
d2.resolve("Pizza");
d3.resolve();
d4.resolve(1, 2, 3);
d5.resolve({ a: "a" });

/** ----------------------------------------------------------------------------------------------------------- */

/**
 * 定義一個很耗時的 wait 函數
 * 這個地方不調用 resolve 就可以執行, 是因爲 wait 並不是一個 promise 或者 deferred 
 * done()方法會立即執行,起不到回調函數的作用
 * $.when()的參數只能是deferred對象,所以必須對wait()進行改寫
 */
var wait = function() {
    var tasks = function() {
        console.log('執行完畢');
    }
    setTimeout(tasks, 2000)
}
$.when(wait())
    .done(function() { console.log("哈哈,成功了!"); })
    .fail(function() { console.log("出錯啦!"); });

/** 
 * 改寫 wait()
 * 再 wait 函數執行完之後, 函數內部調用了成功的方法就會自動運行 done 函數
 * 當然你也可以 調用失敗的方法, 只需要把 `dtd.resolve()` 改成 `dtd.reject()` 即可
 * Deferred 是有三個狀態的
 * 第一個狀態 : 待開始的狀態
 * 第二個狀態 : 成功時的狀態
 * 第三個狀態 : 失敗時的狀態
 */

// 第一種寫法(沒有封裝)
var dtd = $.Deferred() // 創建一個 Deferred 對象
var wait = function(dtd) { // 要求傳入一個 deferred 對象, 不然這個函數沒有意義了
    var tasks = function() {
        //這裏可以有大量的複雜操作
        console.log("執行完畢"); // 執行完畢
        dtd.resolve() // 表示異步任務已經完成, 改變 deferred 對象的狀態

        // dtd.reject() // 表示異步任務失敗, 改變 deferred 對象的狀態
    }
    setTimeout(tasks, 2000) // 等待 2m 後執行
    return dtd // 一定要有返回值, 並且 一定是這個 對象
}
$.when(wait(dtd))
    .done(function() { console.log("哈哈,成功了!"); }) // 哈哈,成功了!
    .fail(function() { console.log("出錯啦!"); });

// // 第二種寫法 (封裝版)
function waitHandle() {
    var dtd = $.Deferred()
    var wait = function(dtd) {
        var tasks = function() {
            console.log('執行完畢') // 執行完畢
            dtd.resolve()

            // dtd.reject()
        }
        setTimeout(tasks, 2000)
        return dtd
    }
    return wait(dtd)
}
var w = waitHandle()

// 此處同樣可是使用 then 方法來調用
w.then(function() {
    console.log('ok 1'); // ok 1
}, function() {
    console.log('err 1');
}).then(function() {
    console.log('ok 1'); // ok 1
}, function() {
    console.log('err 1');
})

/**
 * 這樣呢 : 有一個弊端就是, 可以手動修改狀態.
 * 舉梨 : 比如 我們函數中, 定義的是 dtd.resolve() 方法,但是我們再程序的最後一行加上 `w.reject()` 
 * 結果 就很有意思了, 會再程序以執行的時候, 改變 dtd 的狀態, 然後立即執行 `done()`, 等到 延時器 結束的時候再執行 `console.log('執行完畢')`
 * 這樣就不是我們想要的了
 * 所以 : 就引出了 deferred.promise() 方法.
 */

// + w.resolve()

/** -------------------------------------- deferred.promise() -------------------------------------------------- */

/** 只需要再 wait() 函數裏面
 *  return 的不在是`dtd` 而是 return `dtd.promise`
 *  這樣我們再外邊就沒有辦法修改了
 *  給大家 將一個 思路
 *  就是說 函數裏面的 dtd.resolve 和 dtd.reject 這兩個函數是主動觸發的
 *  還有就是 w.then() 或者 done 還有 fail 這幾個函數是被動監聽的
 */

//  第一種方法
function waitHandle() {
    var dtd = $.Deferred()
    var wait = function(dtd) {
        var tasks = function() {
            console.log('執行完畢') // 執行完畢
            dtd.resolve()

            // dtd.reject()
        }
        setTimeout(tasks, 2000)

        // return dtd
        return dtd.promise() // 返回的不再是 dtd 而是 dtd 的 promise 對象.
    }
    return wait(dtd)
}
var w = waitHandle() // 經過上面的修改, 現在 w 接受的就是一個 promise 對象
w.then(function() {
    console.log('ok 1'); // ok 1
}, function() {
    console.log('err 1');
}).then(function() {
    console.log('ok 1'); // ok 1
}, function() {
    console.log('err 1');
})

w.reject() // 而運行這句話的時候會直接報錯

// 第二種方法. 可以直接在wait對象上部署deferred接口。
var dtd = $.Deferred(); // 生成Deferred對象
var wait = function(dtd) {
    var tasks = function() {
        console.log("執行完畢!");
        dtd.resolve(); // 改變Deferred對象的執行狀態
    };
    setTimeout(tasks, 5000);
};
dtd.promise(wait);
wait.done(function() { console.log("哈哈,成功了!"); })
    .fail(function() { console.log("出錯啦!"); });
wait(dtd);

/**
 * 總結 : deferred 對象的方法
 * 1. $.Deferred()          生成一個新的對象
 * 2. deferred.done()       指定操作成功時的回調
 * 3. deferred.fail()       指定操作失敗時的回調
 * 4. deferred.promise()    沒有參數的時候, 返回一個新的 deferred 對象, 該對象的運行狀態無法被改變; 接受參數時, 作用爲參數對象對象上部署 deferred 接口
 * 5. deferred.resolve()    手動改變 deferred 對象的運行狀態爲 "完成" ,從而立即觸發 done 或者 then 的第一個回調函數
 * 6. deferred.reject()     手動改變 deferred 對象的運行狀態爲 "失敗" ,從而立即觸發 fail 或者 then 的第二個回調函數
 * 7. $.when()              爲單個或多個操作指定回調函數
 * 8. deferred.then()       此方法就是把 done 和 fail 方法的合併, 裏面有兩個參數 第一個時成功時的回調, 第二個時失敗時的回調,可以只寫一個.
 * 9. deferred.always()     這個方法之前沒說過,他的作用是, 不管調用的是 deferred.resolve 還是 deferred.reject, 總是最後才執行
 */
 
$.ajax("test.html")
    .always(function() { alert("已執行!"); });

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章