阿里云「飞天发布时刻」2024来啦!新产品、新特性、新能力、新方案,等你来探~ 了解详情
写点什么

用 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:008158

评论

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

入职阿里!全靠刷明白了这份Java面试合集(分布式+Dubbo+线程+Redis+数据库+JVM+并发)

Java架构之路

Java 程序员 架构 面试 编程语言

华为交换机恢复出厂设置的三种方法

腾讯云大数据发布数据生态战略,构建开源开放数仓生态

腾讯云大数据

大数据 数据仓库

请用思维导图画出架构师训练营所有技术知识点

Jacky.Chen

PlayStation@4功能介绍及测试应用

行者AI

测试

区块链脱虚向实 市场教育基本完成

CECBC

区块链

2020年度国产数据库:openGauss

墨天轮

数据库

音视频技术入门基础

赖猫

c++ 音视频 ffmpeg

架构师训练营11W作业

Geek_f06ede

在onelogin中使用OpenId Connect Authentication Flow

程序那些事

权限系统 OAuth 2.0 程序那些事 权限架构 OpenConnect

腾讯云加速构建云原生数据仓库,助力企业数字化转型

腾讯云大数据

大数据 数据仓库

Java达到什么样的水平才能通过阿里社招?

Java架构师迁哥

Windows Server 做网络转发

wong

Windows Server netsh

看云上 ClickHouse 如何做计算存储分离

腾讯云大数据

大数据 Clickhouse

程序员能靠比特币能致富(暴富)吗?

程序员陆通

比特币 区块链

亿级流量峰值没在怕,“缓存”技术来减压!

博文视点Broadview

小品阅读所带来乐趣

叶小鍵

2021年区块链十大发展趋势:那些偶然中的必然

CECBC

货币

LeetCode题解:347. 前 K 个高频元素,快速排序,JavaScript,详细注释

Lee Chen

算法 大前端 LeetCode

同事临走时,给了我这份多线程and高并发(面试题+思维导图),借此我含泪拿下了阿里offer

Java架构之路

Java 程序员 架构 面试 编程语言

AI面临产业大考:落地虽难,但产业化路径已日渐清晰

脑极体

元旦在家撸了两天Seata源码,你们是咋度过的呢?

冰河

分布式事务 分布式数据库 分布式存储 数据一致性 seata

谷歌被反垄断诉讼后,美国互联网会再度繁荣吗?

脑极体

三年JAVA开发经验,字节四面成功拿下2-2Offer,入职就是30K16薪

Java架构之路

Java 程序员 架构 面试 编程语言

APICloud AVM 多端开发 | 外卖app开发案例教程(下)

YonBuilder低代码开发平台

大前端 Web Worker APICloud

功能测试用例设计方法分享

行者AI

测试

架构师训练营技术知识点

业哥

区块链数据存储与IPFS技术的融合应用

CECBC

区块链 数据存储

Apache Pulsar 12月月报:Pulsar 2.7.0 发布!

Apache Pulsar

大数据 开源 pulsar Apache Pulsar 消息系统

一文彻底吃透MyBatis源码!!

冰河

架构 mybatis 架构设计 框架 源码解析

从技术视角看考拉海购的云原生之路

阿里巴巴中间件

云计算 云原生

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