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加载图片自适应固定大小的DIV
Sep 12 Javascript
容易造成JavaScript内存泄露几个方面
Sep 04 Javascript
jQuery中add()方法用法实例
Jan 08 Javascript
js获取元素的外链样式的简单实现方法
Jun 06 Javascript
JS监听微信、支付宝等移动app及浏览器的返回、后退、上一页按钮的事件方法
Aug 05 Javascript
JSON与String互转的实现方法(Javascript)
Sep 27 Javascript
vue cli 全面解析
Feb 28 Javascript
vue.js或js实现中文A-Z排序的方法
Mar 08 Javascript
npm 下载指定版本的组件方法
May 17 Javascript
JS工厂模式开发实践案例分析
Oct 17 Javascript
js数据类型转换与流程控制操作实例分析
Dec 18 Javascript
ES11屡试不爽的新特性,你用上了几个
Oct 21 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获得url参数中具有&的值的方法
2014/03/05 PHP
优化WordPress中文章与评论的时间显示
2016/01/12 PHP
PHP使用SOAP调用API操作示例
2018/12/25 PHP
PHP+RabbitMQ实现消息队列的完整代码
2019/03/20 PHP
理解javascript对象继承
2016/04/17 Javascript
Bootstrap CSS布局之按钮
2016/12/17 Javascript
Angularjs验证用户输入的字符串是否为日期时间
2017/06/01 Javascript
如何理解Vue的.sync修饰符的使用
2017/08/17 Javascript
jQuery实现输入框的放大和缩小功能示例
2018/07/21 jQuery
详解小程序input框失焦事件在提交事件前的处理
2019/05/05 Javascript
你了解vue3.0响应式数据怎么实现吗
2019/06/07 Javascript
vue的路由映射问题及解决方案
2019/10/14 Javascript
Vue实现简单的拖拽效果
2020/08/25 Javascript
VUE和Antv G6实现在线拓扑图编辑操作
2020/10/28 Javascript
vue内置组件keep-alive事件动态缓存实例
2020/10/30 Javascript
深入解析Python中函数的参数与作用域
2016/03/20 Python
python3实现随机数
2018/06/25 Python
Django密码存储策略分析
2020/01/09 Python
Python基于Dlib的人脸识别系统的实现
2020/02/26 Python
python json 递归打印所有json子节点信息的例子
2020/02/27 Python
django 前端页面如何实现显示前N条数据
2020/03/16 Python
在Mac中配置Python虚拟环境过程解析
2020/06/22 Python
Python实现随机爬山算法
2021/01/29 Python
纯css3显示隐藏一个div特效的具体实现
2014/02/10 HTML / CSS
日本高岛屋百货购物网站:TAKASHIMAYA
2019/03/24 全球购物
主键(Primary Key)约束和唯一性(UNIQUE)约束的区别
2013/05/29 面试题
销售文员的岗位职责
2013/11/20 职场文书
初三物理教学反思
2014/01/21 职场文书
高中生的自我鉴定范文
2014/01/24 职场文书
质量安全标语
2014/06/07 职场文书
运动会搞笑广播稿
2014/10/14 职场文书
小学教师自我评价
2015/03/04 职场文书
离开雷锋的日子观后感
2015/06/09 职场文书
董事长秘书工作总结
2015/08/14 职场文书
JavaScript执行机制详细介绍
2021/12/06 Javascript
Mysql多层子查询示例代码(收藏夹案例)
2022/03/31 MySQL