当前位置 : 主页 > 网页制作 > Dojo >

学习dojo Deferred

来源:互联网 收集:自由互联 发布时间:2021-06-15
开始 当你第一次听到 "Deferred" 这个专业术语, 它就像是一个神秘的对像。 实际上它是一个强大的工具,用于处理异步操作, 例如 ajax. 最简单的一种使用形式是, 一个 "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 指南

资源链表

  • dojo/Deferred Reference Guide
  • dojo/Deferred API
  • Ajax with dojo/request Tutorial
  • Dojo Deferreds and Promises Tutorial
  • Future and Promises Wikipedia article
网友评论