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 利用StringBuffer类提升+=拼接字符串效率
Nov 24 Javascript
jQuery学习笔记之jQuery动画效果
Sep 09 Javascript
javascript判断是否按回车键并解决浏览器之间的差异
May 13 Javascript
javascript实现多级联动下拉菜单的方法
Feb 06 Javascript
深入探讨javascript中的数据类型
Mar 04 Javascript
微信小程序 页面传值详解
Mar 10 Javascript
JS中SetTimeout和SetInterval使用初探
Mar 23 Javascript
微信小程序-横向滑动scroll-view隐藏滚动条
Apr 20 Javascript
angular指令笔记ng-options的使用方法
Sep 18 Javascript
浅析vue-router jquery和params传参(接收参数)$router $route的区别
Aug 03 jQuery
javascript中undefined的本质解析
Jul 31 Javascript
Vue使用Element实现增删改查+打包的步骤
Nov 25 Vue.js
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
全国FM电台频率大全 - 17 湖北省
2020/03/11 无线电
用PHP制作静态网站的模板框架(二)
2006/10/09 PHP
PHP实现MySQL更新记录的代码
2008/06/07 PHP
PHP-CGI进程CPU 100% 与 file_get_contents 函数的关系分析
2011/08/15 PHP
php中静态类与静态变量用法的区别分析
2015/01/15 PHP
PHP+原生态ajax实现的省市联动功能详解
2017/08/15 PHP
PHP CURL使用详解
2019/03/21 PHP
JS 实现Json查询的方法实例
2013/04/12 Javascript
jQuery EasyUI Pagination实现分页的常用方法
2016/05/21 Javascript
JS实现表单多文件上传样式美化支持选中文件后删除相关项
2016/09/30 Javascript
使用PBFunc在Powerbuilder中支付宝当面付款功能
2016/10/01 Javascript
微信小程序  modal详解及实例代码
2016/11/09 Javascript
谈谈Vue.js——vue-resource全攻略
2017/01/16 Javascript
JavaScript通过filereader接口读取文件
2017/05/10 Javascript
JS给按钮添加跳转功能类似a标签
2017/05/30 Javascript
vue mounted组件的使用
2018/06/18 Javascript
webpack+vue-cil中proxyTable处理跨域的方法
2018/07/20 Javascript
Vuejs 实现简易 todoList 功能 与 组件实例代码
2018/09/10 Javascript
angular4 获取wifi列表中文显示乱码问题的解决
2018/10/20 Javascript
vue2.0 实现富文本编辑器功能
2019/05/26 Javascript
JavaScript实现模态对话框实例
2020/01/13 Javascript
JavaScript如何使用插值实现图像渐变
2020/06/28 Javascript
python实现合并两个数组的方法
2015/05/16 Python
Ubuntu下Python2与Python3的共存问题
2018/10/31 Python
Python何时应该使用Lambda函数
2019/07/02 Python
python sklearn常用分类算法模型的调用
2019/10/16 Python
使用celery和Django处理异步任务的流程分析
2020/02/19 Python
详解Python设计模式之策略模式
2020/06/15 Python
python 如何调用远程接口
2020/09/11 Python
利用CSS3实现平移动画效果示例代码
2016/10/12 HTML / CSS
HTML5实现QQ聊天气泡效果
2017/06/26 HTML / CSS
物业管理公司实习生自我鉴定
2013/09/19 职场文书
nginx配置proxy_pass中url末尾带/与不带/的区别详解
2021/03/31 Servers
原生Js 实现的简单无缝滚动轮播图的示例代码
2021/05/10 Javascript
golang特有程序结构入门教程
2021/06/02 Python
进行数据处理的6个 Python 代码块分享
2022/04/06 Python