学习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万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章