阿里、蚂蚁、晟腾、中科加禾精彩分享 AI 基础设施洞见,现购票可享受 9 折优惠 |AICon 了解详情
写点什么

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

评论

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

怎么开发一个貔貅币合约?代码教程全公开

加密先生

Unity 荣膺 2022 鲸鸣奖“影响力出海品牌”及“新势力出海服务商”两项大奖

Geek_2d6073

IoT生态构建:AIoT认证设备中心——实践类

阿里云AIoT

阿里云 物联网 IoT

OpenMLDB SQL 与标准 SQL 的主要差异

第四范式开发者社区

人工智能 机器学习 数据库 开源 特征

从源码角度看React-Hydrate原理

flyzz177

React

适合开发团队的文档管理系统盘点

PingCode

文档管理软件 团队协作管理

BNB Chain 2023年40佳DAPP评选,Zebec赫然在列

鳄鱼视界

假如面试官要你手写一个promise

helloworld1024fd

JavaScript 前端

基于开源IM即时通讯框架MobileIMSDK:RainbowChat-iOS端v6.2版已发布

JackJiang

网络编程 即时通讯 IM

从深度学习框架到开发工具,百度飞桨携最新成绩单亮相 GTC

飞桨PaddlePaddle

英伟达 百度飞桨 GTC

你的聊天室该升级啦!融云平滑迁移方案助你「无感换乘」

融云 RongCloud

通讯

从零手写react-router

helloworld1024fd

JavaScript 前端

深入React源码揭开渲染更新流程的面纱

goClient1992

React

手写一个react,看透react运行机制

goClient1992

React

BNB Chain 2023年40佳DAPP评选,Zebec赫然在列

威廉META

自动化测试工具加入黑科技带来新纪元

石臻臻的杂货铺

人工智能

京东前端二面常考手写面试题(必备)

helloworld1024fd

JavaScript 前端

深度分析React源码中的合成事件

goClient1992

React

云原生消息队列Pulsar浅析——实践类

阿里云AIoT

阿里云 物联网 IoT

一文读懂Vue开发小程序的技术原理

没有用户名丶

十五年,始吾心

博睿数据

可观测性 智能运维 博睿数据 15周年 品牌历史

DockQuery 天狼 v1.2.0 正式发布

BinTools图尔兹

#数据库

一次配置,设备就可实现毫秒级的全球就近接入——实践类

阿里云AIoT

阿里云 物联网 IoT

墨天轮2022年度数据库获奖名单

墨天轮

数据库 opengauss TiDB oceanbase 国产数据库

极光笔记 | 极光PUSH服务助力企业提升抢单速度

极光JIGUANG

技术干货 移动推送 智能推送

Core中本聪主网也能发币了?Core链智能合约教程

加密先生

从recat源码角度看setState流程

flyzz177

React

从react源码看hooks的原理

flyzz177

React

架构作业-8

梁山伯

滴滴前端一面常考手写面试题整理

helloworld1024fd

JavaScript 前端

技术写作的“坎”

码猿外

程序员 写作

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