JavaScript 异步调用框架 (Part 6 - 实例 & 模式)


Posted in Javascript onAugust 04, 2009

封装Ajax
设计Async.Operation的最初目的就是解决Ajax调用需要传递callback参数的问题,为此我们先把Ajax请求封装为Async.Operation。我在这里使用的是jQuery,当然无论你用什么基础库,在使用Async.Operation时都可以做这种简单的封装。

var Ajax = {}; Ajax.get = function(url, data) { 
var operation = new Async.Operation(); 
$.get(url, data, function(result) { operation.yield(result); }, "json"); 
return operation; 
}; 
Ajax.post = function(url, data) { 
var operation = new Async.Operation(); 
$.post(url, data, function(result) { operation.yield(result); }, "json"); 
return operation; 
};

在我所调用的服务器端API中,只需要GET和POST,且数据都为JSON,所以我就直接把jQuery提供的其它Ajax选项屏蔽掉了,并设置数据类型为JSON。在你的项目当中,也可以用类似的方式将Ajax封装为若干仅仅返回Async.Operation的方法,将jQuery提供的选项都封装在Ajax这一层内,不再向上层暴露这些选项。

调用Ajax
把Ajax封装好后,我们就可以开始专心写业务逻辑了。

假设我们有一个Friend对象,它的get方法用于返回单个好友对象,而getAll方法用于返回所有好友对象。于此对应的是两个服务器端API,friend接口会返回单个好友JSON,而friendlist接口会返回所有好友名称组成的JSON。

首先我们看看较为基础的get方法怎么写:

function get(name) { 
return Ajax.get("/friend", "name=" + encodeURIComponent(name)); 
}

就这么简单?对的,假如服务器端API返回的JSON结构正好就是你要的好友对象结构的话。如果JSON结构和好友对象结构是异构的,或许你还要加点代码来把JSON映射为对象:
function get(name) { 
var operation = new Async.Operation() 
Ajax.get("/friend", "name=" + encodeURIComponent(name)) 
.addCallback(function(json) { 
operation.yield(createFriendFromJson(json)); 
}); 
return operation; 
}

Ajax队列
接下来我们要编写的是getAll方法。因为friendlist接口只返回好友名称列表,因此在取得这份列表后我们还要逐一调用get方法获取具体的好友对象。考虑到在同时进行多个friend接口调用可能触发服务器的防攻击策略,导致被关小黑屋一段时间,所以对friend接口的调用必须排队。
function getAll(){ 
var operation = new Async.Operation(); 
var friends = []; 
var chain = Async.chain(); 
Ajax.get("/friendlist", "") 
.addCallback(function(json) { 
for (var i = 0; i < json.length; i++) { 
chain.next(function() { 
return get(json.shift()) 
.addCallback(function(friend) { friends.push(friend); }); 
}); 
} 
chain 
.next(function() { operation.yield(friends); }) 
.go(); 
}) 
return operation; 
}

在这里,我们假设friendlist接口返回的JSON就是一个Array,在获取到这个Array后构造一个等长的异步调用队列,其中每一个调用的逻辑都是一样的——取出Array中首个好友的名称,用get方法获取对应的好友对象,再将好友对象放入另一个Array中。在调用队列的末端,我们再追加了一个调用,用于返回保存好友对象的Array。

在这个例子当中,我们没有利用调用队列会把上一个函数的结果传递给下一个函数的特性,不过也足够展示调用队列的用途了——让多个底层为Ajax请求的异步操作按照固定的顺序阻塞式执行。

由于底层异步函数返回的就是Async.Operation,你可以直接把它传递给next方法,也可以用匿名函数包装后传递给next方法,而匿名函数内部只需要一个return。

延时函数
在上面的例子中,使用队列是为了避免触发服务器的防攻击策略,但有时候这还是不够的。例如说,服务器要求两个请求之间至少间隔500毫秒,否则就认为是攻击,那么我们就要在队列里面插入这个间隔了。

在原本next方法调用的匿名函数中手动加入setTimeout是一个办法,但为什么我们不写一个辅助函数来解决这类问题呢?让我们来写一个辅助方法并让它和Async.Operation无缝结合起来。

Async.wait = function(delay, context) { 
var operation = new Async.Operation(); 
setTimeout(function() { operation.yield(context); }, delay); 
return operation; 
}; Async.Operation.prototype.wait = function(delay, context) { 
this.next(function(context) { return Async.wait(delay, context); }); 
}

在有了这个辅助方法后,我们就可以在上述getAll方法中轻松实现在每个Ajax请求之间间隔500毫秒。在for循环内的加上对wait的调用就可以了。
for (var i = 0; i < json.length; i++) { 
chain 
.wait(500) 
.next(function() { 
return get(json.shift()) 
.addCallback(function(friend) { friends.push(friend); }); 
}); 
}

小结
通过一些简单的例子,我们了解到了Async.Operation常见的使用方式,以及在有需要的时候如何扩展它的功能。希望Async.Operation能够有效帮助大家提高Ajax应用的代码可读性。
Javascript 相关文章推荐
javascript EXCEL 操作类代码
Jul 30 Javascript
跟我一起学写jQuery插件开发方法(附完整实例及下载)
Apr 01 Javascript
ASP.NET jQuery 实例5 (显示CheckBoxList成员选中的内容)
Jan 13 Javascript
JS教程:window.location使用方法的区别介绍
Oct 04 Javascript
jQuery动态改变图片显示大小(修改版)的实现思路及代码
Dec 24 Javascript
深入学习JavaScript中的原型prototype
Aug 13 Javascript
JS实现对中文字符串进行utf-8的Base64编码的方法(使其与Java编码相同)
Jun 21 Javascript
BootStrap入门教程(一)之可视化布局
Sep 19 Javascript
详解Node.js中的事件机制
Sep 22 Javascript
Javascript 普通函数和构造函数的区别
Nov 05 Javascript
详解使用JS如何制作简单的ASCII图与单极图
Mar 31 Javascript
react 创建单例组件的方法
Apr 26 Javascript
javascript 支持链式调用的异步调用框架Async.Operation
Aug 04 #Javascript
JavaScript 异步调用框架 (Part 5 - 链式实现)
Aug 04 #Javascript
JavaScript 异步调用框架 (Part 4 - 链式调用)
Aug 04 #Javascript
JavaScript 异步调用框架 (Part 3 - 代码实现)
Aug 04 #Javascript
JavaScript 异步调用框架 (Part 2 - 用例设计)
Aug 03 #Javascript
JavaScript 异步调用框架 (Part 1 - 问题 &amp; 场景)
Aug 03 #Javascript
jQuery 相关控件的事件操作分解
Aug 03 #Javascript
You might like
PHP设计模式 注册表模式
2012/02/05 PHP
PHP中文字符串截断无乱码解决方法
2016/10/10 PHP
详解将数据从Laravel传送到vue的四种方式
2019/10/16 PHP
TP5框架实现的数据库备份功能示例
2020/04/05 PHP
js获取提交的字符串的字节数
2009/02/09 Javascript
jQuery 1.5.1 发布,全面支持IE9 修复大量bug
2011/02/26 Javascript
构造函数+原型模式构造js自定义对象(最通用)
2014/05/12 Javascript
JavaScript用Number方法实现string转int
2014/05/13 Javascript
jQuery实现“扫码阅读”功能
2015/01/21 Javascript
JavaScript对象数组排序实例方法浅析
2016/06/15 Javascript
Bootstrap Validator 表单验证
2016/07/25 Javascript
json字符串传到前台input的方法
2018/08/06 Javascript
使用validate.js实现表单数据提交前的验证方法
2018/09/04 Javascript
js的各种数据类型判断的介绍
2019/01/19 Javascript
JavaScript中的&quot;=、==、===&quot;区别讲解
2019/01/22 Javascript
Express结合Webpack的全栈自动刷新
2019/05/23 Javascript
layui复选框的全选与取消实现方法
2019/09/02 Javascript
BootstrapValidator实现表单验证功能
2019/11/08 Javascript
深入分析JavaScript 事件循环(Event Loop)
2020/06/19 Javascript
vue中可编辑树状表格的实现代码
2020/10/31 Javascript
JavaScript中遍历的十种方法总结
2020/12/15 Javascript
[01:03:03]VP vs Mineski 2018国际邀请赛淘汰赛BO3 第一场 8.22
2018/08/23 DOTA
go语言计算两个时间的时间差方法
2015/03/13 Python
Python常见加密模块用法分析【MD5,sha,crypt模块】
2017/05/24 Python
Python及Django框架生成二维码的方法分析
2018/01/31 Python
pytorch permute维度转换方法
2018/12/14 Python
用Python实现大文本文件切割的方法
2019/01/12 Python
python Gunicorn服务器使用方法详解
2019/07/22 Python
Python 如何测试文件是否存在
2020/07/31 Python
你不知道的葡萄干处理法、橙蜜处理法、二氧化碳酵母法
2021/03/17 冲泡冲煮
化石印度尼西亚在线商店:Fossil Indonesia
2019/03/11 全球购物
技校学生个人职业生涯规划范文
2014/03/03 职场文书
优秀语文教师事迹
2014/05/18 职场文书
销售2014年度工作总结
2014/12/08 职场文书
2014年环境整治工作总结
2014/12/10 职场文书
2016年五一促销广告语
2016/01/28 职场文书