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 相关文章推荐
jQuery获取动态生成的元素示例
Jun 15 Javascript
使用FlexiGrid实现Extjs表格效果方法分享
Dec 16 Javascript
使用jQuery.form.js/springmvc框架实现文件上传功能
May 12 Javascript
JS组件系列之MVVM组件 vue 30分钟搞定前端增删改查
Apr 28 Javascript
React-router 4 按需加载的实现方式及原理详解
May 25 Javascript
基于vue的短信验证码倒计时demo
Sep 13 Javascript
详解webpack + react + react-router 如何实现懒加载
Nov 20 Javascript
layui 实现加载动画以及非真实加载进度的方法
Sep 23 Javascript
VUE+node(express)实现前后端分离
Oct 13 Javascript
vue 全局环境切换问题
Oct 27 Javascript
加速vue组件渲染之性能优化
Apr 09 Javascript
介绍一下28个JS常用数组方法
May 06 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
工厂模式在Zend Framework中应用介绍
2012/07/10 PHP
zf框架db类的分页示例分享
2014/03/14 PHP
PHP通过内置函数memory_get_usage()获取内存使用情况
2014/11/20 PHP
PHP的cURL库简介及使用示例
2015/02/06 PHP
PHP使用逆波兰式计算工资的方法
2015/07/29 PHP
Symfony2中被遗弃的getRequest()方法分析
2016/03/17 PHP
使用 laravel sms 构建短信验证码发送校验功能
2017/11/06 PHP
javascript 上下banner替换具体实现
2013/11/14 Javascript
Json和Jsonp理论实例代码详解
2013/11/15 Javascript
JS实现模拟百度搜索“2012世界末日”网页地震撕裂效果代码
2015/10/31 Javascript
AngularJS实现星星等级评分功能
2016/09/24 Javascript
微信小程序 ecshop地址三级联动实现实例代码
2017/02/28 Javascript
使用node.js对音视频文件加密的实例代码
2017/08/30 Javascript
jquery实现倒计时小应用
2017/09/19 jQuery
jQuery.Sumoselect插件实现下拉复选框效果
2017/11/09 jQuery
iview在vue-cli3如何按需加载的方法
2018/10/31 Javascript
java和js实现的洗牌小程序
2019/09/30 Javascript
Vue实现腾讯云点播视频上传功能的实现代码
2020/08/17 Javascript
python使用opencv读取图片的实例
2017/08/17 Python
Django使用httpresponse返回用户头像实例代码
2018/01/26 Python
python实现遍历文件夹修改文件后缀
2018/08/28 Python
Python XlsxWriter模块Chart类用法实例分析
2019/03/11 Python
Python中print和return的作用及区别解析
2019/05/05 Python
解决python3插入mysql时内容带有引号的问题
2020/03/02 Python
如何在vscode中安装python库的方法步骤
2021/01/06 Python
土木工程毕业生自荐信
2013/11/12 职场文书
开办化妆品公司创业计划书
2013/12/26 职场文书
协议书模板
2014/04/23 职场文书
大学生党员学习焦裕禄精神思想汇报
2014/09/10 职场文书
咖啡厅商业计划书
2014/09/15 职场文书
自习课吵闹检讨书范文
2014/09/26 职场文书
2015年办公室主任工作总结
2015/04/09 职场文书
入党申请书怎么写?
2019/06/21 职场文书
关于保护环境的建议书
2019/06/24 职场文书
golang中的并发和并行
2021/05/08 Golang
mysql 乱码 字符集latin1转UTF8
2022/04/19 MySQL