继续上一篇文章,这篇探讨使用消息驱动来优化异步编程体验。
先举一个例子,如果希望 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');
});
小结一下:
使用消息驱动的方式可以让我们在异步编程中避免一些回调嵌套的噩梦,优化编程体验,在流程有修改的时候也更加灵活, 可以用一种接近「声明」式的方式去描述异步函数流。
相关阅读:
相关推荐
在《XMPP高级编程——使用JavaScript和jQuery》一书中,作者深入探讨了如何利用这两种强大的Web技术来实现XMPP功能。JavaScript作为浏览器端的主要编程语言,为网页添加动态交互性,而jQuery则简化了DOM操作,提供了...
在"JavaScript动态网页开发详解——源文件"中,我们可以深入学习到JavaScript在网页开发中的应用技巧。此资料可能包含了JQUERY的官方实例全集,jQuery是一个高效、简洁且富有创造性的JavaScript库,它极大地简化了...
事件模式是另一种在JavaScript中处理异步操作的机制,它基于事件驱动的思想,当某个事件被触发时,会执行对应的事件处理函数。 Promises是现代JavaScript中处理异步操作的一个核心概念,它代表了一个尚未完成但预期...
JavaScript中的异步编程模型是基于事件驱动机制的。JavaScript中的异步编程模型可以分为以下几种: 1. 回调函数模型:使用回调函数来执行异步操作。 2. Promise模型:使用Promise来执行异步操作。 3. Async/Await...
1. **回调函数**:JavaScript的基础异步处理方式,但容易导致回调地狱,使得代码难以理解和维护。 2. **Promise**:为解决回调地狱而引入,提供了一种链式调用的方式,使异步代码更易读,但仍有回调问题。 3. **...
6. **异步处理**:如果标注涉及从服务器获取额外信息(如根据坐标查询相关数据),则需要使用AJAX或Fetch API进行异步请求。 7. **用户交互**:提供交互式界面,让用户能编辑、删除或移动已有的标注。这可能需要...
在本教程中,我们将深入探讨如何使用JavaScript来实现省市联动的效果。这种效果常见于许多网站的地址选择器中,当你选择一个省份时,相应的城市列表会自动更新。这个功能能够提高用户体验,使得用户在填写信息时更加...
**Ajax异步传输数据(1)——页面格式** Ajax(Asynchronous JavaScript and XML)是一种在无需重新加载整个网页的情况下,能够更新部分网页的技术。它的核心是JavaScript,利用XMLHttpRequest对象与服务器进行通信,...
JavaScript是Web开发中不可或缺的一部分,尤其在前端领域更是扮演着核心角色。设计模式和异步编程是提升JavaScript代码质量和效率的关键。以下将详细介绍这三本书所涵盖的知识点: 1. **JavaScript设计模式**: ...
JavaScript凌厉开发——Ext详解与实践 源码 源代码 part3 因为源代码比较大,压缩后76M左右 所以分为四个包上传
JavaScript的事件驱动特性在网页开发中至关重要。通过学习DOM(文档对象模型),你可以理解如何操作网页元素,响应用户交互,如点击按钮、滚动页面等。事件监听器、事件处理函数和事件传播机制是这部分的重点。 ...
《JavaScript凌厉开发——Ext JS3详解与实践》是一本深度探讨JavaScript库Ext JS3的专著,旨在帮助开发者深入理解和高效运用这一强大的前端框架。本文将围绕标题、描述及标签,详细介绍Ext JS3的核心概念、关键特性...
JavaScript异步编程是Web开发中的核心概念,尤其在构建高性能、响应式的网页应用时不可或缺。深入理解这一主题,对于任何JavaScript开发者来说都是至关重要的。在这个教程中,我们将探索JavaScript异步处理的各个...
在这个"JavaScript例子——计算"中,我们可以推测这是一篇关于使用JavaScript进行数学计算的教程或者示例代码。博主"Fuhao9611"在iteye博客上分享了这个主题,可能包含了基础的算术运算、自定义函数、循环结构或者...
2. **文件组织结构**:在"集成文件梳理——new.zip"中,文件的命名和组织方式体现了项目的结构和模块化。良好的文件组织能提高团队协作效率,便于理解和维护。文件名通常应该清晰地反映出其内容或功能,例如,"src...
源码包含70个文件,其中包括53个PNG图像文件、5个GIF动画文件、5个JavaScript文件、3个CSS样式表文件、1个Git忽略文件、1个Markdown文件、1个JPG图像文件、1个HTML文件。该设计适用于线上线下多渠道宣传,旨在提升...
在这个场景中,"JSP动态树——javascript" 提供了一个利用JSP和JavaScript实现的交互式树形结构,特别适用于展示层级关系的数据。 1. **JSP基础**: JSP是Java平台的一部分,它允许开发者通过Java编程语言来控制页面...
TodoMVC旨在用各种框架实现TodoList的增、删、改、查功能,麻雀虽小,五脏俱全,是供小伙伴学习、练习、再学习的好例子。 虽然官网上有各种实现版本,但仿佛缺少那么一个版本,就是,就是。。。...
本主题将深入探讨JavaScript中的异步编程,特别是如何在Node.js环境中使用ES2017的异步函数来构建高效的服务器端应用。 在JavaScript中,异步编程通常涉及回调函数、Promise和事件循环。回调函数虽然能够实现非阻塞...