继续上一篇文章,这篇探讨使用消息驱动来优化异步编程体验。
先举一个例子,如果希望 ABCDE 这 5 个函数依次执行,我们可以写出如下代码。
A();
B();
C();
D();
E();
在同步的情况下,这样的代码没有任何问题。 但如果 ABCDE 都是异步的,还需要按次序执行,这样写就不行了。 通常我们会为异步函数设置回调,当函数执行完的时候执行回调,例如
A(function(){
B(function(){
C(function(){
D(function(){
E();
});
});
});
});
毫无疑问这样的编程体验是很差的。 当异步流复杂的时候回调嵌套层数会很多,完全就是一场噩梦。
这还不是最重要的,如果想表达「当 AB 都完成的时候执行 C」这样的流程,并且希望 A/B 可以并行,就不能简单的用这样的回调了。 虽然说「当 AB 都完成的时候执行 C」可以通过设置一个布尔量来解决, 但是「当 ABCD 都完成的时候执行 E」这样的逻辑就需要在每个函数执行完的时候去判断其他函数是否执行完,虽然的确是可行的,但是编程体现是比较差的。
身为一名懒惰的程序员,这样显然满足不了我们的胃口。
@朴灵 写了一个 EventProxy,提供了事件驱动的异步编程体验
var proxy = new EventProxy();
proxy.assign('A', function(){
B(function(){
proxy.trigger('B');
});
});
proxy.assign('B', function(){
C(function(){
proxy.trigger('C');
});
});
proxy.assign('C', function(){
D(function(){
proxy.trigger('D');
});
});
proxy.assign('D', function(){
E();
});
A(function(){
proxy.trigger('A');
});
可以看出通过消息来驱动代码可以让异步嵌套被「拉平」了,而如果要描述「当 ABCD 都完成的时候执行 E」这样的流程也很容易了
var proxy = new EventProxy();
proxy.assign('A', 'B', 'C', 'D', E);
A(function(){
proxy.trigger('A');
});
B(function(){
proxy.trigger('B');
});
C(function(){
proxy.trigger('C');
});
D(function(){
proxy.trigger('D');
});
除了改善异步编程体验以外,EventProxy 也可以提供一个自定义的事件系统。
EventProxy 很简单,源代码只有 300 多行,但是对于我这样的移动开发者来说任何用不上的代码都是负担。
由于我自己将 Event 系统拆成了单独的一个模块,而我(目前为止)也不需要 EventProxy 在 trigger 一个消息的时候的参数传递的功能, 对于 some, any, not 这些限定词我也不需要,因此我自己实现了一个简单版的异步流控制工具。
(function(export){
var uid = 1;
var Jas = function(){
this.map = {};
this.rmap = {};
};
var indexOf = Array.prototype.indexOf || function(obj){
for (var i=0, len=this.length; i<len; ++i){
if (this[i] === obj) return i;
}
return -1;
};
var fire = function(callback, thisObj){
setTimeout(function(){
callback.call(thisObj);
}, 0);
};
Jas.prototype = {
waitFor: function(resources, callback, thisObj){
var map = this.map, rmap = this.rmap;
if (typeof resources === 'string') resources = [resources];
var id = (uid++).toString(16); // using hex
map[id] = {
waiting: resources.slice(0), // clone Array
callback: callback,
thisObj: thisObj
};
for (var i=0, len=resources.length; i<len; ++i){
var res = resources[i],
list = rmap[res] || (rmap[res] = []);
list.push(id);
}
return this;
},
trigger: function(resources){
if (!resources) return this;
var map = this.map, rmap = this.rmap;
if (typeof resources === 'string') resources = [resources];
for (var i=0, len=resources.length; i<len; ++i){
var res = resources[i];
if (typeof rmap[res] === 'undefined') continue;
this._release(res, rmap[res]); // notify each callback waiting for this resource
delete rmap[res]; // release this resource
}
return this;
},
_release: function(res, list){
var map = this.map, rmap = this.rmap;
for (var i=0, len=list.length; i<len; ++i){
var uid = list[i], mapItem = map[uid], waiting = mapItem.waiting,
pos = indexOf.call(waiting, res);
waiting.splice(pos, 1); // remove
if (waiting.length === 0){ // no more depends
fire(mapItem.callback, mapItem.thisObj); // fire the callback asynchronously
delete map[uid];
}
}
}
};
export.Jas = Jas; // Jas is JavaScript Asynchronous (callings) Synchronizer
})(window);
使用起来也挺简单
var flow = new Jas();
flow.waitFor(['A', 'B'], function(){
// both A and B are done!!
});
$.getJSON(url1, function(data){
// An ajax request
flow.trigger('A');
});
$.getJSON(url2', function(data){
// Another ajax request
flow.trigger('B');
});
小结一下:
使用消息驱动的方式可以让我们在异步编程中避免一些回调嵌套的噩梦,优化编程体验,在流程有修改的时候也更加灵活, 可以用一种接近「声明」式的方式去描述异步函数流。
相关阅读:
相关推荐
javascript高级技巧的应用——示例
教你一天玩转JavaScript(三)——使用JavaScript完成图片轮播的效果
教你一天玩转JavaScript(五)——使用JavaScript完成注册页面表单提示及校验
教你一天玩转JavaScript(四)——使用JavaScript实现定时弹出广告定时隐藏广告
教你一天玩转JavaScript(七)——使用JavaScript完成复选框的全选和全不选的效果
教你一天玩转JavaScript(八)——使用JavaScript完成省市联动的效果
Javascript中异步等待的深入理解 在本文中,我们将探讨async/await对于每个Javascript开发人员来说,异步编程的首选工具。如果您不熟悉javascript,请不要担心,本文将帮助您async/await从头开始理解。 async/...
资源JavaScript 函数式编程精要 —— 签约作者安东尼知识分享
XMPP高级编程——使用JavaScript和jQuery,
Javascript定时器(二)——setTimeout与setInterval 在 http://www.cnblogs.com/strick/p/3983904.html 有说明
教你一天玩转JavaScript(六)——使用JavaScript完成后台数据展示表格的隔行换色
全书配套269个JavaScript特效
用html5和javascript实现进程同步模拟——司机售票员问题。在文档后面还附有源代码。
《JavaScript动态网页开发详解》 全书配套 源文件 共21章
本文实例为大家分享了JavaScript实现异步获取表单数据的具体代码,供大家参考,具体内容如下 在上一篇文章中讲到了使用JavaScript异步提交表单中的数据,那么今天我们就讲讲利用JavaScript异步获取表单中的数据;...
全书配套5个视频课件 0 编程起步之Hello_World程序 2 JavaScript事件处理 3 JavaScript基于对象编程 4 JavaScript资源访问安全性 5 JavaScript脚本与Flash进行通信
JavaScript异步编程 设计快速响应的网络应用
JavaScript凌厉开发——Ext详解与实践 源码 源代码 part3 因为源代码比较大,压缩后76M左右 所以分为四个包上传
本资源是《XMPP高级编程——使用JavaScript和JQuery》一书附带的源码,十分详细,搭载在服务器上可直接运行。如果要用自己搭建的Openfire之类的XMPP服务器,修改也十分简单,是学习Web环境下的XMPP协议开发的好资料...
NULL 博文链接:https://dbajun.iteye.com/blog/242763