继续上一篇文章,这篇探讨使用消息驱动来优化异步编程体验。
先举一个例子,如果希望 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在网页开发中的应用技巧。此资料可能包含了JQUERY的官方实例全集,jQuery是一个高效、简洁且富有创造性的JavaScript库,它极大地简化了...
事件模式是另一种在JavaScript中处理异步操作的机制,它基于事件驱动的思想,当某个事件被触发时,会执行对应的事件处理函数。 Promises是现代JavaScript中处理异步操作的一个核心概念,它代表了一个尚未完成但预期...
JavaScript中的异步编程模型是基于事件驱动机制的。JavaScript中的异步编程模型可以分为以下几种: 1. 回调函数模型:使用回调函数来执行异步操作。 2. Promise模型:使用Promise来执行异步操作。 3. Async/Await...
6. **异步处理**:如果标注涉及从服务器获取额外信息(如根据坐标查询相关数据),则需要使用AJAX或Fetch API进行异步请求。 7. **用户交互**:提供交互式界面,让用户能编辑、删除或移动已有的标注。这可能需要...
Promise in js 回调函数真正的问题在于他剥夺了我们使用 return 和 throw 这些关键字的能力。而 Promise 很好地解决了这...所谓 Promise,就是一个对象,用来传递异步操作的消息。它代表了某个未来才会知道结果的事件
1. **回调函数**:JavaScript的基础异步处理方式,但容易导致回调地狱,使得代码难以理解和维护。 2. **Promise**:为解决回调地狱而引入,提供了一种链式调用的方式,使异步代码更易读,但仍有回调问题。 3. **...
在本教程中,我们将深入探讨如何使用JavaScript来实现省市联动的效果。这种效果常见于许多网站的地址选择器中,当你选择一个省份时,相应的城市列表会自动更新。这个功能能够提高用户体验,使得用户在填写信息时更加...
**Ajax异步传输数据(1)——页面格式** Ajax(Asynchronous JavaScript and XML)是一种在无需重新加载整个网页的情况下,能够更新部分网页的技术。它的核心是JavaScript,利用XMLHttpRequest对象与服务器进行通信,...
JavaScript是Web开发中不可或缺的一部分,尤其在前端领域更是扮演着核心角色。设计模式和异步编程是提升JavaScript代码质量和效率的关键。以下将详细介绍这三本书所涵盖的知识点: 1. **JavaScript设计模式**: ...
JavaScript的事件驱动特性在网页开发中至关重要。通过学习DOM(文档对象模型),你可以理解如何操作网页元素,响应用户交互,如点击按钮、滚动页面等。事件监听器、事件处理函数和事件传播机制是这部分的重点。 ...
JavaScript异步编程是Web开发中的核心概念,尤其在构建高性能、响应式的网页应用时不可或缺。深入理解这一主题,对于任何JavaScript开发者来说都是至关重要的。在这个教程中,我们将探索JavaScript异步处理的各个...
JavaScript全面分析——中文版是为想要快速理解和掌握JavaScript编程语言的学者精心编写的教程。JavaScript是一种广泛应用于Web开发的脚本语言,它在浏览器端运行,为网页添加交互性,使得用户界面更加生动活泼。本...
1. 异步数据加载:Jscex可以用于异步加载数据,例如从服务器加载数据或从缓存中读取数据。 2. 网络请求:Jscex可以用于处理网络请求,例如AJAX请求或WebSocket通信。 3. animation和游戏开发:Jscex可以用于...
源码包含70个文件,其中包括53个PNG图像文件、5个GIF动画文件、5个JavaScript文件、3个CSS样式表文件、1个Git忽略文件、1个Markdown文件、1个JPG图像文件、1个HTML文件。该设计适用于线上线下多渠道宣传,旨在提升...
《JavaScript实战手册——第七版代码》是一本专为JavaScript开发者准备的实践指南,它涵盖了从基础到高级的各种JavaScript编程技术。这本书的代码示例旨在帮助读者深入理解语言机制,并提升在实际项目中的应用能力。...
TodoMVC旨在用各种框架实现TodoList的增、删、改、查功能,麻雀虽小,五脏俱全,是供小伙伴学习、练习、再学习的好例子。 虽然官网上有各种实现版本,但仿佛缺少那么一个版本,就是,就是。。。...
总结一下,这个"JavaScript例子——计算"可能会涵盖基本的算术运算、运算符优先级、Math对象的使用、数组操作、类型转换以及可能的自定义函数设计。对于学习和提升JavaScript编程技能,这些都是非常重要的知识点。...
在现代Web应用中,文件上传是一项常见的功能,而“Flash JavaScript异步上传文件——SWFUpload”正是这种功能的一种实现方式。SWFUpload是一个开源的JavaScript库,它结合了Adobe Flash技术,允许用户在不刷新整个...
JavaScript框架高级编程——应用Prototype、YUI、Ext JS、Dojo、MooTools JavaScript框架高级编程——应用Prototype、YUI、Ext JS、Dojo、MooTools
本主题将深入探讨JavaScript中的异步编程,特别是如何在Node.js环境中使用ES2017的异步函数来构建高效的服务器端应用。 在JavaScript中,异步编程通常涉及回调函数、Promise和事件循环。回调函数虽然能够实现非阻塞...