dojo 1.6 官方教程:dojo.Deferred 初探

作者: Bryan Forbes

译者: feijia (tiimfei@gmail.com)

原文连接: http://dojotoolkit.org/documentation/tutorials/1.6/deferreds/

适用dojo 版本: 1.6

难度: 中等

在本教程中, 你将学到使用Dojo的 deffer(延迟) 方法的基本知识。

使用延迟方法,可以优雅轻松的处理在JS编程中常见的异步调用。我们还将解释如何使用DeferredList,它能够一次处理多个延迟的异步调用。

入门

初听起来, “延迟" (Deferred) 这个名字颇为神秘。但它其实是一个Dojo提供给我们用来处理异步调用(例如Ajax)的强大工具. 简单来说,一个Deferred对象它会等待一段时间再去执行指定的调用,直到某个特定的事件发生或者前一个动作完成。Ajax就是一种常见的例子: 发出一个Ajax请求之后,我们需要等待服务器把响应返回之后才会调用处理函数。所以Deferred对象提供给我们的就是这种将某个动作延迟并等待某个事件发生的能力。在本教程中,我们将会结合Ajax教程的内容一起来解读如何使用Deferred对象更好的管理异步的JS程序。

dojo.Deferred

延迟的概念并非Dojo专有,(译者:Jquery 等其他JS框架也有类似的对象和实现, CommonJS 组织还为此定义了一套标准 ), 从0.3 版本开始Dojo就实现了dojo.Deferred对象。

Deferred对象有三个状态,初始化时是"unresolve” 状态,当它所等待的事件发生时, 进入"resolve" 状态, 第三种状态是出错状态,即该事件没有按照预期的发展,例如服务器端返回了错误消息,也称reject 状态.

创建Deferred对象后,我们可以通过调用该对象的then方法注册一个回调函数,表示当这个Deferred对象等待的某个事件发生时(resolve),就调用该回调函数。then 方法还接受第二个参数,可以制定当事件失败或出错时(reject)时调用的出错回调函数 .

让我们来看一个例子:

var def = new dojo.Deferred(), userlist = dojo.byId("userlist"); def.then(function(res){ // This will be called when the deferred // is resolved dojo.forEach(res, function(user){ dojo.create("li", { id: user.id, innerHTML: user.username + ": " + user.name }, userlist); }); },function(err){ // This will be called when the deferred // is rejected dojo.create("li", { innerHTML: "Error: " + err }, userlist); }); dojo.xhrGet({ url: "users.json", handleAs: "json", load: function(res){ // Resolve when content is received def.resolve(res); }, error: function(err){ // Reject on error def.reject(err); } });

查看示例

在上面的示例中,我们创建了一个dojo.Deferred 对象并在上面分别注册了一个成功回调函数和出错回调函数。我们还调用了dojo.xhrGet 一个异步Ajax调用,去服务器端获取"user.json"。 如果这个ajax调用成功了,我们在xhr对象的load属性所设的回调函数中会将dojo.Deferred对象置为resolve状态 ,这时我们在该Deferred对象上注册的回调函数将会被调用;如果ajax调用失败,则Deferred上注册的错误回调函数将会被调用。

上面的例子,你可能会觉得是多此一举, 为什么不直接在xhrGet里直接分别设定成功和失败的回调函数呢? 是的,你的确可以这么做,但是通过引入Deffered对象,我们把负责处理服务器端返回数据的逻辑(回调函数)和发送Ajax请求的逻辑进行了解藕。

实际上,为了方便开发者使用Deffered对象,Dojo的Ajax构造函数方法会直接返回给你一个Deferred对象,因此上面的代码可以简化不少:

var def = dojo.xhrGet({ url: "users.json", handleAs: "json" }); def.then(function(res){ var userlist = dojo.byId("userlist"); dojo.forEach(res, function(user){ dojo.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); });

查看示例

在这个例子中我们不再需要设置dojo.xhrGet的 load属性了,可以直接在xhrGet返回的Deferred对象上通过then来注册回调函数. 代码逻辑更加直观简洁.

在回调函数中,我们遍历了从服务器端返回的用户列表,并且为每个用户创建了一个HTML列表 。 从功能上看,和前一个例子完全一样,但是在这个例子中,我们得以把处理服务器端数据的逻辑和发送Ajax请求的逻辑分开了。 所以Deferred对象的一个重要功能就是对我们的程序逻辑进行解藕。 (decoupling)

链式调用

dojo.Deferred是个挺容易理解的概念,但是它还有一些很强大的功能值得我们继续探索. 其中之一就是链式调用(Chaining):每个then方法的返回值都仍然是一个Defered对象。 我们来看一个例子:

假设前面的例子里服务器端返回的不是JSON格式的用户对象,而是每个用户的信息的原始值。 值当然不如对象方便使用,所以我们希望注册一个回调函数来把这些原始数值转换为用户对象。

var original = dojo.xhrGet({ return dojo.map(res, function(user){ dojo.create("li", { innerHTML: dojo.toJson(user) }, userlist); return { id: user[0], username: user[1], name: user[2] }; }); }); // 由original的then方法返回的result对象也有一个`then` 方法来接受回调函数,和original对象一样。 // 但是要注意的是传给result.then中注册的回调函数的值,不是Ajax调用获取的数据, 而是original的回调函数的返回值。 // 也就是已经经过格式转换的用户对象map result.then(function(objs){ var userlist = dojo.byId("userlist2"); dojo.forEach(objs, function(user){ dojo.create("li", { innerHTML: dojo.toJson(user) }, userlist); }); });

注意: 严格来说then方法的返回值并不是一个Deferred对象,它有个特定的名字"promise", 即承诺,实现了一个特定的API. 你可以进一步阅读关于prommise的教程 来深入学习, 不过在这里,我们可以暂时理解为 一个promise对象提供了和Deferred对象完全相同的then方法。因此Deferred对象和Promise对象的then方法可以进行连续的链式调用。这样做的好处是什么呢?链式调用时,原始的Defered对象不会被修改,而且服务器端的返回的数据也没有被修改,你可以继续在original的defered对象上注册其他的回调函数来对原始数据进行进一步操作,在前面的例子基础上,你可以注册一个新的回调到original上,例如:

original.then(function(res){ var userlist = dojo.byId("userlist3"); dojo.forEach(res, function(user){ dojo.create("li", { innerHTML: dojo.toJson(user) }, userlist); }); });

查看示例

我们进一步可以把上面的例子改成:

function getUserList(){ // 注意我们这里不是返回xhrGet获取到的数据, // but of the .then call on that xhrGet's return return dojo.xhrGet({ url: "users-mangled.json", handleAs: "json" }).then(function(res){ return dojo.map(res, function(user){ return { id: user[0], username: user[1], name: user[2] }; }); }); } getUserList().then(function(users){ var userlist = dojo.byId("userlist"); dojo.forEach(users, function(user){ dojo.create("li", { innerHTML: dojo.toJson(user) }, userlist); }); });

通过这样封装, 使用getUserList的方法就总是能获取到一个已经处理好的用户列表了。 (实验: 反复调用会导致多次ajax调用么?)

dojo.DeferredList

有时,我们需要同时从多个不同的来源获取数据,当这些数据全部到位后我们希望可以被通知到。Dojo也提供了方便的封装来辅助你完成这一工作,这就是dojo.DeferredList对象。

使用dojo.DeferredList时, 只要把一组Deferred对象(作为数组)传入它的构造函数,它会返回给你一个新的Deferred对象。 在此对象上,你可以注册一个回调函数,当该回调函数被调用时,原始的deferred对象的结果会作为参数被传递进入该回调函数。参数是一个tuple数组,(tuple就是一个二元数值对)。数值对中第一个数值是一个boolean表示该Deferred是成功还是失败,第二个数值是该Deferred的返回值。 让我们看一个例子:

dojo.require("dojo.DeferredList"); dojo.ready(function(){ // 第一个Ajax请求,产生一个defferred 对象: userDef var usersDef = dojo.xhrGet({ url: "users.json", handleAs: "json" }).then(function(res){ var users = {}; dojo.forEach(res, function(user){ users[user.id] = user; }); return users; }); // 另一个Ajax请求,产生第二个defferred 对象: statusesDef var statusesDef = dojo.xhrGet({ url: "statuses.json", handleAs: "json" }); //利用两个Defferred对象构造一个DefferredList对象 var defs = new dojo.DeferredList([usersDef, statusesDef]); //DeferredList 对象也有一个then方法用来注册回调函数,回调函数的参数是一个tuple构成的数组 // 该回调函数只有当前DefferredList所包含的所有Deferred对象进入Resolved或者Error状态后才会调用。 defs.then(function(results){ // 每个tuple的第二个值,是相对应的deferred注册的回调函数的返回值, var users = results[0][1], statuses = results[1][1], statuslist = dojo.byId("statuslist"); // 每个tuple的第一个值,是一个boolean,表示该Deffered对象所代表的请求是否成功了 即deffered 是否成功resolved if(!results[0][0] || !results[1][0]){ dojo.create("li", { innerHTML: "An error occurred" }, statuslist); return; } dojo.forEach(statuses, function(status){ var user = users[status.userId]; dojo.create("li", { id: status.id, innerHTML: user.name + ' said, "' + status.status + '"' }, statuslist); }); }); });

这个例子中我们想从服务器端分别获取用户信息和用户的状态。 我们使用了一个DeferrredList来等待这两个请求都完成后进行处理,回调函数先检查两个请求是否都成功完成了,如果没有发生任何错误,则遍历两个请求分别获得的用户数据和状态数据。 回调函数只有当两个请求都进入完成状态后才会被调用,因此我们不用关心究竟是哪个请求先完成了。

(设想一下,如果没有DeferredList,你需要如何手动处理这个情形?)

查看示例

结论

绝大多数JavaScript应用都需要使用Ajax,因此它们都需要注册异步的回调函数。dojo.Deferred就提供了这样一个简单又优雅的方法。 它可以非常灵活的进行链式调用;而使用dojo.DeferredList又可以让你处理个Deferred对象。

分三个包上传时,第三个包好像传不上去,我给整合了一下,打在一个包里上传了! dojo精品中文教程 Dojo.1.0 Practice Note [1] 什么是dojo 选择dojo的理由 AJAX架构之Dojo篇 Adding Ajax中文版 (DoJo) DOJO学习笔记(七)-日期控件DropdownDatePicker和DatePicker DOJO常用的验证函数 Dojo with Adobe AIR Dojo 工具包教程 Dojo 快速安装 DojoJSON建立无限级AJAX动态加载的功能模块树 Dojo学习笔记( 模块与包) Dojo学习笔记-- djConfig解说 Dojo学习笔记-- dojo.dom Dojo学习笔记-- dojo.event & dojo.event.topic & dojo.event.browser Dojo学习笔记--DateTextbox Dojo学习笔记--Dojo的基础对象和方法 Dojo学习笔记--FisheyeList鱼眼效果 Dojo学习笔记--TabContainer Dojo学习笔记--ValidationTextbox Dojo学习笔记--dijit.Dialog Dojo学习笔记--dijit.Menu Dojo学习笔记--dijit.TitlePane Dojo学习笔记--dijit.Tooltip Dojo学习笔记--dijit.Tree Dojo学习笔记--dojo.graphics.color & dojo.uri.Uri Dojo学习笔记--dojo.string & dojo.lang Dojo学习笔记--动态生成widget Dojo学习笔记--开发自己的TitlePane Dojo学习笔记--页面部分区域遮挡,DialogUnderlay Dojo学习笔记(五)-djConfig详解 dojo data 接口详解 dojo0.9 使用心得 dojo学习笔记(一)-dojo.io.IO & dojo.io.BrowserIO) dojo学习笔记(三) dojo学习笔记(二) dojo.lang.array & dojo.lang.func & dojo.string.extras dojo学习笔记(六)- ContentPane dojo学习笔记(四) dojo的拖拽示例以及疑问! 介绍dojo事件 使用 Dojo 工具包和 JSON-RPC 构建企业 SOA Ajax 客户端 利用Dojo实现拖动(Drag and Drop)效果
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值