學習dojo Deferred

開始

當你第一次聽到 "Deferred" 這個專業術語, 它就像是一個神祕的對像。 實際上它是一個強大的工具,用於處理異步操作, 例如 ajax.  最簡單的一種使用形式是, 一個 "Deferred" 用於一段時間之後在執行某一個行爲。 本質上來說,你可以推遲一個動作的執行,直到它預先定義的動畫已經完成。 比如Ajax中, 我們可以初始化一個XMLRequest請求服務器資源,但在服務器返回資源時,我們不會在做任何動作。 直到有返回值,在執行定義的動作。 本教程,我們將結何Dojo Ajax 指南 , 來學習如何使用Deferreds。

dojo/Deferred

dojo 的 Deffered 的實現是通過 dojo/Deferred模塊(0.3版本以來就有了),在Dojo 1.8 對這個模塊進行了重構。 我們在下面的代碼裏,實例化一個Deferred,  可以通過then方法給它添加一個即將在未來發生的行爲或者回調函數。 當Deferred被解析成功(ajax成功返回值) 後, 這些行爲或回調函數就會被調用。 then可以接受第二個參數,當Deferred被拒絕(Ajax發生錯誤),調用的函數,這個錯誤回調被稱爲 errback.  通過代碼, 我們可以有更好的理解. 
require(["dojo/Deferred", "dojo/request", "dojo/_base/array", "dojo/dom-construct", "dojo/dom", "dojo/domReady!"],
    function(Deferred, request, arrayUtil, domConstruct, dom) {
 
        // Create a deferred and get the user list
        var deferred = new Deferred(),
            userlist = dom.byId("userlist");
 
        // Set up the callback and errback for the deferred
        deferred.then(function(res){
            arrayUtil.forEach(res, function(user){
                domConstruct.create("li", {
                    id: user.id,
                    innerHTML: user.username + ": " + user.name
                }, userlist);
            });
        },function(err){
            domConstruct.create("li", {
                innerHTML: "Error: " + err
            }, userlist);
        });
 
        // Send an HTTP request
        request.get("users.json", {
            handleAs: "json"}).then(
            function(response){
                // Resolve when content is received
                deferred.resolve(response);
            },
            function(error){
                // Reject on error
                deferred.reject(error);
            }
        );
});

查看demo

在這個例子中, 我們創建了一個Deferred, 並且給它註冊了成功resolve後的回調函數和一個errback.  我們調用request.get (異步操作). 來檢索服務器上的 'user.json'. 如果檢索成功, 它將調用 deferred.resolve,告訴deferred可以執行它註冊的回調函數。如果檢索失敗,會觸發一個reject 信號,deferred就會調用 then中第二個errback.

你可能會問自己,”我每次都要這樣設置dojo/Deferred模塊嘛?",  其實不用,所有的Dojo Ajax方法都會返回 dojo/promise/Promise, 它也是在成功返回是,被resolve,  在發生錯誤時,被rejected.
require(["dojo/request", "dojo/_base/array", "dojo/dom-construct", "dojo/dom", "dojo/domReady!"],
    function(request, arrayUtil, domConstruct, dom) {
 
        var deferred = request.get("users.json", {
            handleAs: "json"
        });
 
        deferred.then(function(res){
            var userlist = dom.byId("userlist");
 
            arrayUtil.forEach(res, function(user){
                domConstruct.create("li", {
                    id: user.id,
                    innerHTML: user.username + ": " + user.name
                }, userlist);
            });
        },function(err){
            // This shouldn't occur, but it's defined just in case
            alert("An error occurred: " + err);
        });
 
});

我們通過then方法註冊一個回調, 如果Ajax 請求成功, Deferred 會被resolved , 並且第一個參數會被調用,如果ajax調用失敗, Deferred會被rejected, 並且有一個error會被傳遞給errback.

我們來說一下, 爲什麼要使用Deferred, 之前使用過Jquery的同學都知道, jquery是只能在ajax初始化的時個, 設置一個success屬性作爲回調。 而使用Deferred的時候, 我們可以有更好的解耦, 不用在ajax中調置回調, dojo  可以將ajax作爲一個Deferred返回,然後在任意位置決定是否要添加then方法,來註冊回調函數。

Deferred的鏈式調用

雖然Deferred是的一個非常簡單的概念, 但它還是包含了很多強大的功能。 比如, 每次調用then,實際上它會返回一個新的Deferred對像, 而不是callback的返回值。  這聽起來很迷惑, 讓我們看一個例子。

我們之前,json的數據爲[{id: 1, username: 'xxx', name: 'aaa'}, {id: 2, username: 'xxx', name: 'aaa'}],  用對像表示一個用戶的信息,現在我們替換成用數組表示一個用戶的信息[ [1, 'xxx', 'aaa'], [2, 'xxx', 'aaa'] ]。 這種表示方法不是很常用, 所以我們需要註冊一個回調函數, 將數組轉化成一對用戶對像。  依賴於第一個 then調用返回的結果(返回一個promise), 新返回的promise上註冊的回調將使用的是user 對像,而不是數組了。 還是很難理解,直接看代碼
require(["dojo/request", "dojo/_base/array", "dojo/json", "dojo/dom-construct", "dojo/dom", "dojo/domReady!"],
    function(request, arrayUtil, JSON, domConstruct, dom) {
 
        var original = request.get("users-mangled.json", {
            handleAs: "json"
        });
 
        var result = original.then(function(res){
            var userlist = dom.byId("userlist1");
 
            return arrayUtil.map(res, function(user){
                domConstruct.create("li", {
                    innerHTML: JSON.stringify(user)
                }, userlist);
 
                return {
                    id: user[0],
                    username: user[1],
                    name: user[2]
                };
            });
        });
 
        // result 對像有一個`then`方法,它可以接受回調函數,
        // 這跟 original (ajax返回的對你)是一樣的  -- 但是回調函數處理的數據
        // 不在是Ajax的返回值, 而是第一次回調函數的返回值
        result.then(function(objs){
            var userlist = dom.byId("userlist2");
 
            arrayUtil.forEach(objs, function(user){
                domConstruct.create("li", {
                    innerHTML: JSON.stringify(user)
                }, userlist);
            });
        });
});

備註:then 方法的返回值是一個承諾(promise), 它實現的特定的API. 你可以通過 promise 指南,瞭解更多的細節, 但現在, 只需要知道promise也是提供了then方法,這與Deferred對像的then方法相同。

有一個很重要的點,需要注意: original Deferred(ajax反回的對像) 沒有受到鏈式調用的影響,  依然保持 ajax返回的數組

original.then(function(res){
    var userlist = dom.byId("userlist3");
 
    arrayUtil.forEach(res, function(user){
        domConstruct.create("li", {
            innerHTML: JSON.stringify(user)
        }, userlist);
    });
});

查看Demo

以上的例子在偏激, 但如果你的應用程序確實需要修改數據,你可以使用如下的方法
require(["dojo/request", "dojo/_base/array", "dojo/dom-construct", "dojo/dom", "dojo/domReady!"],
    function(request, arrayUtil, domConstruct, dom) {
 
        function getUserList(){
            return request.get("users-mangled.json", {
                handleAs: "json"
            }).then(function(response){
                return arrayUtil.map(response, function(user){
                    return {
                        id: user[0],
                        username: user[1],
                        name: user[2]
                    };
                });
            });
        }
 
        getUserList().then(function(users){
            var userlist = dom.byId("userlist");
            arrayUtil.forEach(users, function(user){
                domConstruct.create("li", {
                    id: user.id,
                    innerHTML: user.username + ": " + user.name
                }, userlist);
            });
        });
});

查看Demo

現在通過getUserList就可以獲得 user objects, 而不是數組了


有時,你可能需要從多個源中並行檢索數組, 並且在所有的請求完成後,可以獲得通知。  或者串行的Deferred, 並且計算返回值。  你可以使用 dojo/DeferredList模塊。 在1.8版本中,是通過dojo/promise/all 和 dojo/promise/first實現, 你可以學習 promises 指南

資源鏈表

發佈了44 篇原創文章 · 獲贊 11 · 訪問量 13萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章