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 相关文章推荐
一个可绑定数据源的jQuery数据表格插件
Jul 17 Javascript
JS中setTimeout()的用法详解
Apr 14 Javascript
使用Jquery实现每日签到功能
Apr 03 Javascript
微信小程序 http请求详细介绍
Oct 09 Javascript
获取select的value、text值的简单示例(jquery与javascript)
Dec 07 Javascript
详解JS对象封装的常用方式
Dec 30 Javascript
微信小程序去哪里找 小程序到底如何使用(附小程序名单)
Jan 09 Javascript
ionic2中使用自动生成器的方法
Mar 04 Javascript
layer.open 按钮的点击事件关闭方法
Aug 17 Javascript
微信小程序框架的页面布局代码
Aug 17 Javascript
浅谈Vue为什么不能检测数组变动
Oct 14 Javascript
Vuex实现简单购物车
Jan 10 Vue.js
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+.htaccess实现全站静态HTML文件GZIP压缩传输(一)
2007/02/15 PHP
PHP遍历某个目录下的所有文件和子文件夹的实现代码
2013/06/28 PHP
destoon实现调用当前栏目分类及子分类和三级分类的方法
2014/08/21 PHP
PHP实现根据银行卡号判断银行
2015/04/29 PHP
将PHP从5.3.28升级到5.3.29时Nginx出现502错误
2015/05/09 PHP
php判断当前操作系统类型
2015/10/28 PHP
详解php 使用Callable Closure强制指定回调类型
2017/10/26 PHP
JAVASCRIPT对象及属性
2007/02/13 Javascript
jQuery powerFloat万能浮动层下拉层插件使用介绍
2010/12/27 Javascript
JavaScript去掉数组中的重复元素
2011/01/13 Javascript
jQuery 网易相册鼠标移动显示隐藏效果实现代码
2013/03/31 Javascript
javascript伸缩型菜单实现代码
2015/11/16 Javascript
学好js,这些js函数概念一定要知道【推荐】
2017/01/19 Javascript
全面解析jQuery中的$(window)与$(document)的用法区别
2017/08/15 jQuery
vue-cli 组件的导入与使用教程详解
2018/04/11 Javascript
使用js实现将后台传入的json数据放在前台显示
2018/08/06 Javascript
vue中父子组件的参数传递和应用示例
2021/01/04 Vue.js
用Python进行一些简单的自然语言处理的教程
2015/03/31 Python
解决Python中由于logging模块误用导致的内存泄露
2015/04/23 Python
Python yield 使用浅析
2015/05/28 Python
Python使用os模块和fileinput模块来操作文件目录
2016/01/19 Python
Python 序列的方法总结
2016/10/18 Python
利用scrapy将爬到的数据保存到mysql(防止重复)
2018/03/31 Python
python爬虫租房信息在地图上显示的方法
2019/05/13 Python
python opencv 图像拼接的实现方法
2019/06/27 Python
解决python tkinter界面卡死的问题
2019/07/17 Python
pycharm双击无响应(打不开问题解决办法)
2020/01/10 Python
大学本科生的个人自我评价
2013/12/09 职场文书
公司保密承诺书
2014/03/27 职场文书
工程承包协议书
2014/04/22 职场文书
音乐学专业求职信
2014/07/22 职场文书
殡葬服务心得体会
2014/09/11 职场文书
2014年语文教研组工作总结
2014/12/06 职场文书
安全生产先进个人事迹材料
2014/12/30 职场文书
发票退票证明
2015/06/24 职场文书
win10双系统怎么删除一个系统?win10电脑有两个系统删除一个的操作方法
2022/07/15 数码科技