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和Ajax中文乱码吐血版解决方案
Dec 21 Javascript
javascript 主动派发事件总结
Aug 09 Javascript
chrome下img加载对height()的影响示例探讨
May 26 Javascript
Bootstrap每天必学之折叠
Apr 12 Javascript
jquery 动态增加,减少input表单的简单方法(必看)
Oct 12 Javascript
使用grunt合并压缩js和css文件的方法
Mar 02 Javascript
详解vue-router 2.0 常用基础知识点之导航钩子
May 10 Javascript
JavaScript比较同一天的时间大小实例代码
Feb 09 Javascript
Vue内部渲染视图的方法
Sep 02 Javascript
解决Vue调用springboot接口403跨域问题
Sep 02 Javascript
小程序怎样让wx.navigateBack更好用的方法实现
Nov 01 Javascript
vue实现输入一位数字转汉字功能
Dec 13 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删除二维数组中相同元素及数组重复值的方法示例
2017/05/05 PHP
NodeJS与Mysql的交互示例代码
2013/08/18 NodeJs
基于JavaScript实现鼠标悬浮弹出跟随鼠标移动的带箭头的信息层
2016/01/18 Javascript
你不知道的高性能JAVASCRIPT
2016/01/18 Javascript
jQuery中$.each()函数的用法引申实例
2016/05/12 Javascript
js获取form表单所有数据的简单方法
2016/08/18 Javascript
使用vue.js实现联动效果的示例代码
2017/01/10 Javascript
bootstrap fileinput 上传插件的基础使用
2017/02/17 Javascript
jQuery实现IE输入框完成placeholder标签功能的方法
2017/09/20 jQuery
vuex vue简单使用知识点总结
2019/08/29 Javascript
你不可不知的Vue.js列表渲染详解
2019/10/01 Javascript
JS页面获取 session 值,作用域和闭包学习笔记
2019/10/16 Javascript
Javascript 关于基本类型和引用类型的个人理解
2019/11/01 Javascript
微信小程序实现一个简单swiper代码实例
2019/12/30 Javascript
[02:40]DOTA2英雄基础教程 巨牙海民
2013/12/23 DOTA
详解Python中的序列化与反序列化的使用
2015/06/30 Python
requests和lxml实现爬虫的方法
2017/06/11 Python
详解tensorflow载入数据的三种方式
2018/04/24 Python
详解Python中is和==的区别
2019/03/21 Python
Python用字典构建多级菜单功能
2019/07/11 Python
pytorch中的embedding词向量的使用方法
2019/08/18 Python
Python中sorted()排序与字母大小写的问题
2020/01/14 Python
浅谈Python的方法解析顺序(MRO)
2020/03/05 Python
django自带的权限管理Permission用法说明
2020/05/13 Python
使用Keras预训练好的模型进行目标类别预测详解
2020/06/27 Python
Python如何测试stdout输出
2020/08/10 Python
Python之字典对象的几种创建方法
2020/09/30 Python
兰蔻美国官网:Lancome美国
2017/04/25 全球购物
开放系统互连参考模型
2016/06/29 面试题
社会保险接收函
2014/01/12 职场文书
总经理岗位职责范本
2014/02/02 职场文书
宣传活动总结范文
2014/07/01 职场文书
2014年卫生监督工作总结
2014/12/09 职场文书
学习型家庭事迹材料
2014/12/20 职场文书
2015年班长个人工作总结
2015/04/03 职场文书
2015年计算机教师工作总结
2015/07/22 职场文书