立即领取|华润集团、宁德核电、东风岚图等 20+ 标杆企业数字化人才培养实践案例 了解详情
写点什么

用 Dojo 实现 Ajax 请求:XHR、跨域、及其他

  • 2011-01-18
  • 本文字数:6762 字

    阅读完需:约 22 分钟

在任何浏览器上方便地实现 Ajax 请求是每一个 Ajax 框架的初衷。Dojo 在这方面无疑提供了非常丰富的支持。除了 XMLHttpRequest 之外,动态 script、iframe、RPC 也应有尽有,并且接口统一,使用方便,大多数情况下都只需要一句话就能达到目的,从而免除重复造轮子的麻烦。而且,Dojo 一贯追求的概念完整性也在这里有所体现,换句话说,在使用 Dojo 的 Ajax 工具的过程中不会感到任何的不自然,相反更容易有触类旁通的感觉,因为 API 的模式是统一的,而且这里涉及到的某些概念(如 Deferred 对象)也贯穿在整个 Dojo 之中。

Dojo 的 XHR 函数

Dojo 的 XMLHttpRequest 函数就叫 dojo.xhr,除了把自己取名美元符号之外,这好像是最直接的办法了。它定义在 Dojo 基本库里,所以不需要额外的 require 就能使用。它可以实现任何同域内的 http 请求。不过更常用的是 dojo.xhrGet 和 dojo.xhrPost,它们只不过是对 dojo.xhr 函数的简单封装;当然根据 REST 风格,还有 dojo.xhrPut 和 dojo.xhrDelete。

这些函数的参数都很统一。除了 dojo.xhr 的第一个参数是 http 方法名之外,所有的 dojo.xhr* 系列函数都接受同一种散列式的参数,其中包含请求的细节,例如 url、是否同步、要传给服务器的内容(可以是普通对象、表单、或者纯文本)、超时设定、返回结果的类型(非常丰富且可扩展)、以及请求成功和失败时的回调。所有 dojo.xhr* 函数(实际上是所有 IO 函数)返回值也都一样,都是一个 Deferred 对象,顾名思义,它能让一些事情“延迟”发生,从而让 API 用起来更灵活。

下面的两个例子可能会带来一点直观感受:

复制代码
dojo.xhrGet({
url: "something.html",
load: function(response, ioArgs){
// 用 response 干一些事
console.log("xhr get success:", response);
return response; // 必须返回 response
},
error: function(response, ioArgs){
console.log("xhr get failed:", response);
return response; // 必须返回 response
}
});
//Deferred 对象允许用同步调用的写法写异步调用
var deferredResult = dojo.xhrPost({
url: "something.html",
form: formNode, //Dojo 会自动将 form 转成 object
timeout: 3000, //Dojo 会保证超时设定的有效性
handleAs: "json" // 得到的 response 将被认为是 JSON,并自动转为 object
});
// 当响应结果可用时再调用回调函数
deferredResult.then(function(response){
console.log("xhr get success:", response);
return response; // 必须返回 response
});

首先解释一下 timeout。除了 IE8 之外,目前大多数 XMLHttpRequest 对象都没有内置的 timeout 功能,因此必须用 setTimeout。当同时存在大量请求时,需要为每一个请求设置单独的定时器,这在某些浏览器(主要是 IE)会造成严重的性能问题。dojo 的做法是只用一个单独的 setInterval,定时轮询(间隔 50ms)所有还未结束的请求的状态,这样就高效地解决了一切远程请求(包括 JSONP 和 iframe)的超时问题。

值得一提的还有 handleAs 参数,通过设置这个参数,可以自动识别服务器的响应内容格式并转换成对象或文本等方便使用的形式。根据文档,它接受如下值:text (默认), json, json-comment-optional, json-comment-filtered, javascript, xml。

而且它还是可扩展的。其实 handleAs 只是告诉 xhr 函数去调用哪个格式转换插件,即 dojo.contentHandlers 对象里的一个方法。例如 dojo.contentHandlers.json 就是处理 JSON 格式的插件。你可以方便地定制自己所需要的格式转换插件,当然,你也可修改现有插件的行为:

复制代码
dojo.contentHandlers.json = (function(old){
return function(xhr){
var json = old(xhr);
if(json.someSignalFormServer){
doSomthing(json);
delete json.someSignalFormServer;
}
return json;
}
})(dojo.contentHandlers.json);// 一个小技巧,利用传参得到原方法

如果要了解每个参数的细节,可以参考 Dojo 的文档

虚拟的参数类

这里特别提一下 Dojo 在 API 设计上的两个特点。其一是虚拟的参数“类”概念:通过利用 javascript 对象可以灵活扩展的特点,强行规定一个散列参数属于某个“类”。例如 dojo.xhr* 系列函数所接受的参数就称为 dojo.__XhrArgs。这个“类”并不存在于实际代码中(不要试图用 instanceof 验证它),只停留在概念上,比抽象类还抽象,因此给它加上双下划线前缀(Dojo 习惯为抽象类加单下划线前缀)。这样做看起来没什么意思,但实际上简化了 API,因为它使 API 之间产生了联系,更容易记忆也就更易于使用。这一点在对这种类做“继承”时更明显。例如 dojo.__XhrArgs 继承自 dojo.__IoArgs,这是所有 IO 函数所必须支持的参数集合,同样继承自 dojo.__IoArgs 的还有 dojo.io.script.__ioArgs 和 dojo.io.iframe.__ioArgs,分别用于动态脚本请求和 iframe 请求。子类只向父类添加少量的属性,这样繁多的参数就具有了树形类结构。原本散列式参数是用精确的参数名代替了固定的参数顺序,在增加灵活性和可扩展性的同时,实际上增加了记忆量(毕竟参数名不能拼错),使得 API 都不像看起来那么好用,有了参数类的设计就缓解了这个问题。

这种参数类的做法在 Dojo 里随处可见,读源码的话就会发现它们都是被正儿八经地以正常代码形式声明在一种特殊注释格式里的,像这样:

复制代码
/*=====
dojo.declare("dojo.__XhrArgs", dojo.__IoArgs, {
constructor: function(){
//summary:
//...
//handleAs:
//...
//......
}
});
=====*/

这种格式可以被 jsDoc 工具自动提取成文档,在文档里这些虚拟出来的类就像真的类一样五脏俱全了。

Deferred 对象

另一个 API 设计特点就是 Deferred 对象的广泛使用。Dojo 里的 Deferred 是基于 MochiKit 实现稍加改进而成的,而后者则是受到 python 的事件驱动网络工具包 Twisted 里同名概念的启发。概括来说的话,这个对象的作用就是将异步 IO 中回调函数的声明位置与调用位置分离,这样在一个异步 IO 最终完成的地方,开发人员可以简单地说“货已经到了,想用的可以来拿了”,而不用具体地指出到底该调用哪些回调函数。这样做的好处是让异步 IO 的写法和同步 IO 一样(对数据的处理总是在取数据函数的外面,而不是里面),从而简化异步编程。

具体做法是,异步函数总是同步地返回一个代理对象(这就是 Deferred 对象),可以将它看做你想要的数据的代表,它提供一些方法以添加回调函数,当数据可用时,这些回调函数 (可以由很多个) 便会按照添加顺序依次执行。如果在取数据过程中出现错误,就会调用所提供的错误处理函数 (也可以有很多个);如果想要取消这个异步请求,也可通过 Deferred 对象的 cancel 方法完成。

dojo.Deferred 的核心方法如下:

复制代码
then(callback, errback); // 添加回调函数
callback(result); // 表示异步调用成功完成,触发回调函数
errback(error); // 表示异步调用中产生错误,触发错误处理函数
cancel(); // 取消异步调用

Dojo 还提供了一个 when 方法,使同步的值和异步的 Deferred 对象在使用时写法一样。例如:

复制代码
// 某个工具函数的实现
var obj = {
getItem: function(){
if(this.item){
return this.item; // 这里同步地返回数据
}else{
return dojo.xhrGet({  // 这里返回的是 Deferred 对象
url: "toGetItem.html",
load: dojo.hitch(this, function(response){
this.item = response;
return response;
})
});
}
}
};
// 用户代码
dojo.when(obj.getItem(), function(item){
// 无论同步异步,使用工具函数 getItem 的方式都一样
});

在函数闭包的帮助下,Deferred 对象的创建和使用变得更为简单,你可以轻易写出一个创建 Deferred 对象的函数,以同步的写法做异步的事。例如写一个使用 store 获取数据的函数:

复制代码
var store = new dojo.data.QueryReadStore({...});
function getData(start, count){
var d = new dojo.Deferred(); // 初始化一个 Deferred 对象
store.fetch({
start: start,
count: count,
onComplete: function(items){
// 直接取用上层闭包里的 Deferred 对象
d.callback(items);
}
});
return d; // 把它当做结果返回
}

用 dojo.io.script 跨域

dojo.xhr* 只是 XmlHttpRequest 对象的封装,由于同源策略限制,它不能发跨域请求,要跨域还是需要动态创建

2011-01-18 00:008284

评论

发布
暂无评论
发现更多内容

MySQL 数据库开发入门(四):MySQL 的数据引擎

程序员小呆

MySQL MySQL 高可用 :MySQL 数据库 MySQL 数据库 java

阿里最受追捧的,中高级技术核心,助我拿下菜鸟offer,附面经

程序员小呆

Java 程序员 面试 架构师 java面试

我从外包辞职了,10000小时后,拿了字节跳动的offer!

Java 程序员 架构 面试 后端

终于进了字节!记录我作为一名程序媛磕磕碰碰的三个月找工作经历

Java 程序员 架构 面试 后端

自定义 View:如何手动绘制一个头像控件

Changing Lin

10月月更

全网首发!马士兵内部共享—1658页《Java面试突击核心讲》

收到请回复

Java 程序员 面试 架构师 金九银十

流行技术限时开源!Alibaba新产“Java面试权威指南”助阵金九银十

Java 编程 程序员 架构 面试

推荐 7 个 yyds 的开源项目

开源 编程 架构 计算机

实体链接在OPPO小布助手和OGraph的实践应用

OPPO小布助手

人工智能 智能助手 nlu 语音助手 自然语言理解

无敌是多么的寂寞!这份在各大平台获百万推荐的Java核心手册称得上史上最强!

Java 架构 面试 程序人生 编程语言

OceanBase 存储层代码解读(一)引言

OceanBase 数据库

oceanbase OceanBase 开源 OceanBase 社区版 OceanBase 数据库大赛

我一口气面试6家大厂,已拿下5家offer,分享经验和Java资料,其实大厂没有你想象中难!

程序员小呆

Java 程序员 面试 架构师 java面试

解读clickhouse存算分离在华为云实践

华为云开发者联盟

数据库 Clickhouse OBS 华为云 存算分离

Alibaba高级架构师撰写的SQL笔记,不止收获SQL优化更能抓住SQL的本质

Java 架构 面试 程序人生 编程语言

MySQL 数据库开发入门(一):安装与常用命令

程序员小呆

014云原生之云数据库

穿过生命散发芬芳

云原生 10月月更

GitHub爆火!阿里内部Java高并发系统设计全彩手册曝光,极致的理解!

Java 架构 面试 程序人生 编程语言

AtomSolutions与Bholdus缔结业务合作伙伴关系

Geek_c610c0

为 Elasticsearch/Kibana 配置账号和 https(qbit)

qbit

https 安全 TLS ssl

百度翻译团队获“2021年大数据科技传播奖”未来奖

百度大脑

人工智能

第 15 章 -《Linux 一学就会》- LVM管理和ssm存储管理器使用

学神来啦

Linux 运维 ssm lvm linux云计算

本以为能躺着进华为,结果陆续收到京东/滴滴/爱奇艺offer的我迷茫了

Java spring 算法 编程语言

如何做好Code Review

百度开发者中心

最佳实践 方法论 工程能力

MySQL 数据库开发入门(三):Navicat 客户端的使用

程序员小呆

MySQL MySQL 高可用 MySQL 运维 :MySQL 数据库 MySQL 数据库

字节跳动等10+公司面经+面试题+答案分享! 35K不是梦

程序员小呆

Java 程序员 面试 架构师 java面试

Java 异常机制

码语者

Java Exception 异常机制

2021年阿里巴巴最新Java面试学习资料汇总:从基础到高级、框架、数据库、多线程并发知识、分布式、以及企业的面试真题

Java 编程 程序员 架构 面试

区块链技术赋能音乐作品版权保护 法院采信存证证书作为有效权属证据

CECBC

靠这份1500道面试题的资料,助我拿下7家大厂offer !其中一家是美团

程序员小呆

Java 程序员 架构师 java面试

阿里大牛开源内部"JDK源码手册"一经现世,惊艳四方

Java 架构 面试 程序人生 编程语言

MySQL 数据库开发入门(二):数据库表的创建与管理

程序员小呆

MySQL MySQL 高可用 MySQL 运维 MySQL 数据库 java

用Dojo实现Ajax请求:XHR、跨域、及其他_Java_朱小文_InfoQ精选文章