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

封装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应用的代码可读性。

(0)

相关推荐

  • javascript 支持链式调用的异步调用框架Async.Operation

    复制代码 代码如下: Async = {}; Async.Operation = function(options) { options = options || {}; var callbackQueue = []; var chain = (options.chain && options.chain === true) ? true : false; var started = false; var innerChain = null; this.result = undefined

  • JavaScript 异步调用

    问题 可修改下面的 aa() 函数,目的是在一抄后用 console.log() 输出 want-value function aa() { setTimeout(function() { return "want-value"; }, 1000); } 但是,有额外要求: aa() 函数可以随意修改,但是不能有 console.log() 执行 console.log() 语句里不能有 setTimeout 包裹 解答 也许这是个面试题,管它呢.问题的主要目的是考察对异步调用执行结果

  • JavaScript异步调用定时方法并停止该方法实现代码

    复制代码 代码如下: (function() { var t = window.setInterval(function() { $('#ajaxGame').load('RefreshCurrentPlayerServlet #ajaxGame', function() { youWin(); youlose(); }); }, 10000); function youWin() { if ($('#status:contains("YOU ARE THE WINNER!")').l

  • JavaScript 异步调用框架 (Part 1 - 问题 & 场景)

    问题 在Ajax应用中,调用XMLHttpRequest是很常见的情况.特别是以客户端为中心的Ajax应用,各种需要从服务器端获取数据的操作都通过XHR异步调用完成.然而在单线程的JavaScript编程中,XHR异步调用的代码风格实在是与一般的JavaScript代码格格不入. 额外参数 考虑一个除法函数,如果它是纯客户端的同步函数,那么签名会是这样的: function divide(operand1, operand2) 然而假设我们对客户端除法的精度不满意,于是把除法转移到服务器端来执行

  • Android与JS之间跨平台异步调用实例详解

    Android与JS之间跨平台异步调用 为什么突然要搞这个问题呢? 在开发浏览器的时候遇到这个狗血的问题,花了将近1天的时间才想到这个解决方案,Android与JavaScirpt互调. 因为接口是抓取的别人的,所以出现了JS跨域问题,Android闪亮登场搞定了. GIF动画演示   WebView相关设置 WebSettings mWebSettings = getSettings(); mWebSettings.setDefaultTextEncodingName("UTF-8"

  • JavaScript 异步调用框架 (Part 5 - 链式实现)

    调用入口 链式调用存在Async.go方法和Async.chain方法两个入口,这两个入口本质上是一致的,只是Async.chain方法在调用时先不提供初始参数,而Async.go方法在调用时提供了初始参数并启动异步调用链. 复制代码 代码如下: Async.chain = function() { var chain = new Async.Operation({ chain: true }); return chain; }; Async.go = function(initialArgum

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

    封装Ajax 设计Async.Operation的最初目的就是解决Ajax调用需要传递callback参数的问题,为此我们先把Ajax请求封装为Async.Operation.我在这里使用的是jQuery,当然无论你用什么基础库,在使用Async.Operation时都可以做这种简单的封装. 复制代码 代码如下: var Ajax = {}; Ajax.get = function(url, data) { var operation = new Async.Operation(); $.get

  • JavaScript 异步调用框架 (Part 3 - 代码实现)

    类结构 首先我们来搭一个架子,把需要用到的似有变量都列出来.我们需要一个数组,来保存回调函数列表:需要一个标志位,来表示异步操作是否已完成:还可以学IAsyncResult,加一个state,允许异步操作的实现者对外暴露自定义的执行状态:最后加一个变量保存异步操作结果. 复制代码 代码如下: Async = { Operation: { var callbackQueue = []; this.result = undefined; this.state = "waiting"; th

  • JavaScript 异步调用框架 (Part 4 - 链式调用)

    现实开发中,要按顺序执行一系列的同步异步操作又是很常见的.还是用百度Hi网页版中的例子,我们先要异步获取联系人列表,然后再异步获取每一个联系人的具体信息,而且后者是分页获取的,每次请求发送10个联系人的名称然后取回对应的具体信息.这就是多个需要顺序执行的异步请求. 为此,我们需要设计一种新的操作方式来优化代码可读性,让顺序异步操作代码看起来和传统的顺序同步操作代码一样优雅. 传统做法 大多数程序员都能够很好的理解顺序执行的代码,例如这样子的: 复制代码 代码如下: var firstResult

  • JavaScript 异步调用框架 (Part 2 - 用例设计)

    传递回调 我们首先要考虑的一个问题是,如何传递回调入口.在最传统的XHR调用当中,回调函数会被作为最后一个参数传递给异步函数: 复制代码 代码如下: function asyncOperation(argument, callback) 在参数相当多的时候,我们可以把参数放到一个JSON里面,这样参数就如同具名参数一样,可以通过参数名选择性的传递参数,不传递的参数相当于使用默认值.这是从Prototype开始就流行起来的做法: 复制代码 代码如下: function asyncOperation

  • Unity同步/异步调用Android的方法实例

    目录 1 Unity如何使用Android插件 2 同步方法如何调用 2.1 例子1 2.2 例子2 3 异步方法如何调用 3.1 Android端 3.1.1 添加依赖 3.1.2 代码逻辑 3.2 Unity端 总结 1 Unity如何使用Android插件 很简单,把android的模块,打包成aar或者jar,放到Assets/Plugin/Android即可! 2 同步方法如何调用 2.1 例子1 Android端 假设Android有一个方法 public static String

随机推荐