JavaScript 异步调用框架实例与模式:Async.Operation 实战

作者:异步漫步时间:2026-10-23 21:38:58浏览:477

本文介绍如何使用 Async.Operation 封装 jQuery Ajax 请求,解决回调地狱问题。通过封装 GET/POST 方法统一返回 Operation 对象,并结合 Async.chain 实现有序异步队列。文章还展示了如何扩展 wait 方法处理请求间隔,以规避服务器防攻击策略,提升代码可读性与稳定性。

封装 Ajax

Async.Operation 封装 Ajax 请求

设计 Async.Operation 的核心初衷,在于消除 Ajax 调用中必须显式传递回调函数的繁琐。为此,第一步是将底层的 Ajax 请求封装为 Async.Operation 对象。本文示例基于 jQuery 库,但无论使用何种基础库,均可采用相同思路进行封装。


具体实现代码如下:

 
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));
} 

实现看似简单,前提是服务器返回的 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 数组,获取该数组后构造一个等长的异步调用队列。队列中每个调用的逻辑一致:取出数组首个好友名称,通过 get 方法获取对应对象,并将其推入结果数组。在队列末端追加一个调用,用于返回保存了所有好友对象的数组。



本例未利用调用队列传递上一函数结果的特性,但已充分展示队列用途:使多个底层为 Ajax 请求的异步操作按固定顺序阻塞式执行。



由于底层异步函数直接返回 Async.Operation,可直接将其传递给 next 方法;也可用匿名函数包装后传递,此时匿名函数内部仅需一个 return 语句。



延时函数

扩展 wait 方法处理请求间隔

上述例子使用队列旨在避免触发服务器防攻击策略,但有时仍需更精细的控制。例如,若服务器要求两个请求间至少间隔 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 应用的代码可读性。

作者最新文章