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


Posted in Javascript onAugust 04, 2009

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

var firstResult = firstOperation(initialArgument); 
var secondResult = secondOperation(firstResult); 
var finalResult = thirdOperation(secondResult); 
alert(finalResult);

其中先执行的函数为后执行的函数提供所需的数据。然而使用我们的异步调用框架后,同样的逻辑必须变成这样子:
firstAsyncOperation(initialArgument).addCallback(function(firstResult) { 
secondAsyncOperation(firstResult).addCallback(function(secondResult) { 
thirdAsyncOperation(secondResult).addCallback(function(finalResult) { 
alert(finalResult); 
}); 
}); 
});

链式写法
我认为上面的代码实在是太不美观了,并且希望能够改造为jQuery风格的链式写法。为此,我们先构造一个用例:
Async.go(initialArgument) 
.next(firstAsyncOperation) 
.next(secondAsyncOperation) 
.next(thirdAsyncOperation) 
.next(function(finalResult) { alert(finalResult); })

在这个用例当中,我们在go传入初始化数据,然后每一个next后面传入一个数据处理函数,这些处理函数按顺序对数据进行处理。
同步并存
上面的用例调用到的全部都是异步函数,不过我们最好能够兼容同步函数,让使用者无需关心函数的具体实现,也能使用这项功能。为此我们再写一个这样的用例:
Async.go(0) 
.next(function(i) { alert(i); return i + 1; }) 
.next(function(i) { 
alert(i); 
var operation = new Async.Operation(); 
setTimeout(function() { operation.yield(i + 1); }, 1000); 
return operation; 
}) 
.next(function(i) { alert(i); return i + 1; }) 
.next(function(i) { alert(i); return i; });

在上述用例中,我们期待能够看到0, 1, 2, 3的提示信息序列,并且1和2之间间隔为1000毫秒。
异步本质
一个链式调用,本质上也是一个异步调用,所以它返回的也是一个Operation实例。这个实例自然也有result、state和completed这几个字段,并且当整个链式调用完成时,result等于最后一个调用返回的结果,而completed自然是等于true。
我们可以扩展一下上一个用例,得到如下用例代码:
var chainOperation = Async.go(0) 
.next(function(i) { alert(i); return i + 1; }) 
.next(function(i) { 
alert(i); 
var operation = new Async.Operation(); 
setTimeout(function() { operation.yield(i + 1); }, 1000); 
return operation; 
}) 
.next(function(i) { alert(i); return i + 1; }) 
.next(function(i) { alert(i); return i; }); 
setTiemout(function() { alert(chainOperation.result; }, 2000);

把链式调用的返回保存下来,在链式调用完成时,它的result应该与最后一个操作的返回一致。在上述用例中,也就是3。
调用时机
尽管我们提供了一种链式调用方式,但是用户不一定会按照这种固定的方式来调用,所以我们仍然要考虑兼容用户的各种可能用法,例如说异步地用next往调用链添加操作:
var chainOperation = Async.go(0); 
chainOperation.next(function(i) { alert(i); return i + 1; }); 
setTimeout(function() { 
chainOperation.next(function(i) { 
alert(i); 
var operation = new Async.Operation(); 
setTimeout(function() { operation.yield(i + 1); }, 2000); 
return operation; 
}) 
}, 1000); 
setTimeout(function() { 
chainOperation.next(function(i) { alert(i); return i + 1; }); 
}, 2000);

在这个用例当中,用户每隔1000毫秒添加一个操作,而其中第二个操作耗时2000毫秒。也就是说,添加第三个操作时第二个操作还没返回。作为一个健壮的框架,必须要能兼容这样的使用方式。
此外我们还要考虑,用户可能想要先构造调用链,然后再执行调用链。这时候用户就会先使用next方法添加操作,再使用go方法执行。
var chainOperation = Async 
.chain(function(i) { alert(i); return i + 1; }) 
.next(function(i) { 
alert(i); 
var operation = new Async.Operation(); 
setTimeout(function() { operation.yield(i + 1); }, 2000); 
return operation; 
}) 
.go(0) 
setTimeout(function() { 
chainOperation.next(function(i) { alert(i); return i + 1; }) 
}, 1000);

在上述用例中,用户通过chain和next添加了头同步操作和异步操作各一个,然后用go执行调用链,在调用链执行完毕之前又用next异步追加了一个操作。一个健壮的框架,在这样的用例当中应该能够如同用户所期望的那样提示0, 1, 2。
小结
针对链式调用的需求,我们设计了如此多的用例,包括各种奇怪的异步调用方式。最终如何实现这样的功能呢?
Javascript 相关文章推荐
javascript 树控件 比较好用
Jun 11 Javascript
Javascript 面向对象(二)封装代码
May 23 Javascript
JS中把字符转成ASCII值的函数示例代码
Nov 21 Javascript
js如何判断用户是在PC端和还是移动端访问
Apr 24 Javascript
60行js代码实现俄罗斯方块
Mar 31 Javascript
JS获得一个对象的所有属性和方法实例
Feb 21 Javascript
几行js代码实现自适应
Feb 24 Javascript
Angular.js中window.onload(),$(document).ready()的写法浅析
Sep 28 Javascript
原生js封装的ajax方法示例
Aug 02 Javascript
vue结合element-ui使用示例
Jan 24 Javascript
JS实现集合的交集、补集、差集、去重运算示例【ES5与ES6写法】
Feb 18 Javascript
Vue中对iframe实现keep alive无刷新的方法
Jul 23 Javascript
JavaScript 异步调用框架 (Part 3 - 代码实现)
Aug 04 #Javascript
JavaScript 异步调用框架 (Part 2 - 用例设计)
Aug 03 #Javascript
JavaScript 异步调用框架 (Part 1 - 问题 & 场景)
Aug 03 #Javascript
jQuery 相关控件的事件操作分解
Aug 03 #Javascript
利用javascript实现一些常用软件的下载导航
Aug 03 #Javascript
jQuery 隔行换色 支持键盘上下键,按Enter选定值
Aug 02 #Javascript
一句话JavaScript表单验证代码
Aug 02 #Javascript
You might like
joomla实现注册用户添加新字段的方法
2016/05/05 PHP
php 获取xml接口数据的处理方法
2018/05/31 PHP
Yii框架核心组件类实例详解
2019/08/06 PHP
使用JavaScript switch case 另类写法
2010/03/14 Javascript
firefox下frameset取不到值的解决方法
2010/09/06 Javascript
JAVASCRIPT函数作用域和提前声明 分享
2013/08/22 Javascript
Node.js中创建和管理外部进程详解
2014/08/16 Javascript
兼容主流浏览器的JS复制内容到剪贴板
2014/12/12 Javascript
js实现的简单radio背景颜色选择器代码
2015/08/18 Javascript
JS实现快递单打印功能【推荐】
2018/06/21 Javascript
Promise.all中对于reject的处理方法
2018/08/01 Javascript
vue+element-ui集成随机验证码+用户名+密码的form表单验证功能
2018/08/05 Javascript
Node.js系列之发起get/post请求(2)
2019/08/30 Javascript
详解node.js创建一个web服务器(Server)的详细步骤
2021/01/15 Javascript
python中使用urllib2获取http请求状态码的代码例子
2014/07/07 Python
简单介绍Python中的struct模块
2015/04/28 Python
Python实现邮件的批量发送的示例代码
2018/01/23 Python
对python GUI实现完美进度条的示例详解
2018/12/13 Python
详解python使用turtle库来画一朵花
2019/03/21 Python
Python3 shelve对象持久存储原理详解
2020/03/23 Python
Python collections.defaultdict模块用法详解
2020/06/18 Python
python实现图片转字符画的完整代码
2021/02/21 Python
Nº21官方在线商店:numeroventuno.com
2019/09/26 全球购物
用C#语言写出与SQLSERVER访问时的具体过程
2013/04/16 面试题
恶搞卫生巾广告词
2014/03/18 职场文书
航海技术专业毕业生求职信
2014/04/06 职场文书
给校长的建议书300字
2014/05/16 职场文书
作风转变心得体会
2014/09/02 职场文书
专题民主生活会对照检查材料思想汇报
2014/09/29 职场文书
2014年企业党支部工作总结
2014/12/04 职场文书
校长师德表现自我评价
2015/03/05 职场文书
电影地道战观后感
2015/06/04 职场文书
百善孝为先:关于孝道的经典语录
2019/10/18 职场文书
JavaScript实现班级抽签小程序
2021/05/19 Javascript
Python办公自动化PPT批量转换操作
2021/09/15 Python
Python中的turtle画箭头,矩形,五角星
2022/03/16 Python