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 相关文章推荐
Ext.FormPanel 提交和 Ext.Ajax.request 异步提交函数的区别
Nov 12 Javascript
弹出层之1:JQuery.Boxy (一) 使用介绍
Oct 06 Javascript
Select标签下拉列表二级联动级联实例代码
Feb 07 Javascript
jQuery+PHP星级评分实现方法
Oct 02 Javascript
jQuery+canvas实现的球体平抛及颜色动态变换效果
Jan 28 Javascript
jQuery实现百叶窗焦点图动画效果代码分享(附源码下载)
Mar 14 Javascript
javascript定时器取消定时器及优化方法
Jul 08 Javascript
JavaScript中call和apply方法的区别实例分析
Aug 03 Javascript
vue template中slot-scope/scope的使用方法
Sep 06 Javascript
vue实现父子组件之间的通信以及兄弟组件的通信功能示例
Jan 29 Javascript
详解在vue-cli3.0中自定css、js和图片的打包路径
Aug 26 Javascript
Vue 实现拨打电话操作
Nov 16 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
一个PHP模板,主要想体现一下思路
2006/12/25 PHP
phpmyadmin出现Cannot start session without errors问题解决方法
2014/08/14 PHP
微信公众平台开发关注及取消关注事件的方法
2014/12/23 PHP
PHP用户注册邮件激活账户的实现代码
2017/05/31 PHP
PHP htmlentities()函数用法讲解
2019/02/25 PHP
浏览器无法运行JAVA脚本的解决方法
2008/01/09 Javascript
JavaScript小技巧 2.5 则
2010/09/12 Javascript
JS对img进行操作(换图片/切图/轮换/停止)
2013/04/17 Javascript
文字不间断滚动(上下左右)实例代码
2013/04/21 Javascript
判断ie的两种简单方法
2013/08/12 Javascript
js Array操作的最简短最容易理解方法
2013/12/09 Javascript
jquery动态添加删除一行数据示例
2014/06/12 Javascript
ECMAScript6块级作用域及新变量声明(let)
2015/06/12 Javascript
JavaScript实现页面跳转的几种常用方式
2015/11/28 Javascript
JavaScript构造函数详解
2015/12/27 Javascript
javascript解决小数的加减乘除精度丢失的方案
2016/05/31 Javascript
jQuery+PHP实现上传裁剪图片
2020/06/29 jQuery
vue-devtools的安装和使用步骤详解
2019/10/17 Javascript
vue或react项目生产环境去掉console.log的操作
2020/09/02 Javascript
python实现根据用户输入从电影网站获取影片信息的方法
2015/04/07 Python
Python对list列表结构中的值进行去重的方法总结
2016/05/07 Python
Python实现字符串的逆序 C++字符串逆序算法
2020/05/28 Python
python爬取内容存入Excel实例
2019/02/20 Python
在SQLite-Python中实现返回、查询中文字段的方法
2019/07/17 Python
Django项目中使用JWT的实现代码
2019/11/04 Python
英国虚拟主机服务商:eUKhost
2016/08/16 全球购物
澳大利亚牛仔裤商店:Just Jeans
2016/10/13 全球购物
印尼极简主义和实惠的在线家具店:Fabelio
2019/03/27 全球购物
司机辞职报告范文
2014/01/20 职场文书
大学竞选班干部演讲稿
2014/08/21 职场文书
小学生推普周国旗下讲话稿
2014/09/21 职场文书
2014年护士长工作总结
2014/11/11 职场文书
新生开学寄语大全
2015/05/28 职场文书
2019年教师节活动策划方案
2019/09/09 职场文书
javascript数组includes、reduce的基本使用
2021/07/02 Javascript
java固定大小队列的几种实现方式详解
2021/07/15 Java/Android