javascript异步编程的4种方法


Posted in Javascript onFebruary 19, 2014

你可能知道,Javascript语言的执行环境是"单线程"(single thread)。
所谓"单线程",就是指一次只能完成一件任务。如果有多个任务,就必须排队,前面一个任务完成,再执行后面一个任务,以此类推。
这种模式的好处是实现起来比较简单,执行环境相对单纯;坏处是只要有一个任务耗时很长,后面的任务都必须排队等着,会拖延整个程序的执行。常见的浏览器无响应(假死),往往就是因为某一段Javascript代码长时间运行(比如死循环),导致整个页面卡在这个地方,其他任务无法执行。
为了解决这个问题,Javascript语言将任务的执行模式分成两种:同步(Synchronous)和异步(Asynchronous)。
"同步模式"就是上一段的模式,后一个任务等待前一个任务结束,然后再执行,程序的执行顺序与任务的排列顺序是一致的、同步的;"异步模式"则完全不同,每一个任务有一个或多个回调函数(callback),前一个任务结束后,不是执行后一个任务,而是执行回调函数,后一个任务则是不等前一个任务结束就执行,所以程序的执行顺序与任务的排列顺序是不一致的、异步的。
"异步模式"非常重要。在浏览器端,耗时很长的操作都应该异步执行,避免浏览器失去响应,最好的例子就是Ajax操作。在服务器端,"异步模式"甚至是唯一的模式,因为执行环境是单线程的,如果允许同步执行所有http请求,服务器性能会急剧下降,很快就会失去响应。
一、回调函数

这是异步编程最基本的方法。

假定有两个函数f1和f2,后者等待前者的执行结果。

f1();
f2();

如果f1是一个很耗时的任务,可以考虑改写f1,把f2写成f1的回调函数。

function f1(callback){
setTimeout(function () {
// f1的任务代码
callback();
}, 1000);
}

 

执行代码就变成下面这样:

f1(f2);采用这种方式,我们把同步操作变成了异步操作,f1不会堵塞程序运行,相当于先执行程序的主要逻辑,将耗时的操作推迟执行。

回调函数的优点是简单、容易理解和部署,缺点是不利于代码的阅读和维护,各个部分之间高度耦合(Coupling),流程会很混乱,而且每个任务只能指定一个回调函数。

二、事件监听

另一种思路是采用事件驱动模式。任务的执行不取决于代码的顺序,而取决于某个事件是否发生。

还是以f1和f2为例。首先,为f1绑定一个事件(这里采用的jQuery的写法)。

f1.on('done', f2);

上面这行代码的意思是,当f1发生done事件,就执行f2。然后,对f1进行改写:

function f1(){
setTimeout(function () {
// f1的任务代码
f1.trigger('done');
}, 1000);
}

f1.trigger('done')表示,执行完成后,立即触发done事件,从而开始执行f2。

这种方法的优点是比较容易理解,可以绑定多个事件,每个事件可以指定多个回调函数,而且可以"去耦合"(Decoupling),有利于实现模块化。缺点是整个程序都要变成事件驱动型,运行流程会变得很不清晰。

三、发布/订阅

上一节的"事件",完全可以理解成"信号"。

我们假定,存在一个"信号中心",某个任务执行完成,就向信号中心"发布"(publish)一个信号,其他任务可以向信号中心"订阅"(subscribe)这个信号,从而知道什么时候自己可以开始执行。这就叫做"发布/订阅模式"(publish-subscribe pattern),又称"观察者模式"(observer pattern)。

这个模式有多种实现,下面采用的是Ben Alman的Tiny Pub/Sub,这是jQuery的一个插件。

首先,f2向"信号中心"jQuery订阅"done"信号。

jQuery.subscribe("done", f2);

然后,f1进行如下改写:
function f1(){
setTimeout(function () {
// f1的任务代码
jQuery.publish("done");
}, 1000);
}

 

jQuery.publish("done")的意思是,f1执行完成后,向"信号中心"jQuery发布"done"信号,从而引发f2的执行。

此外,f2完成执行后,也可以取消订阅(unsubscribe)。

jQuery.unsubscribe("done", f2);

这种方法的性质与"事件监听"类似,但是明显优于后者。因为我们可以通过查看"消息中心",了解存在多少信号、每个信号有多少订阅者,从而监控程序的运行。

四、Promises对象

Promises对象是CommonJS工作组提出的一种规范,目的是为异步编程提供统一接口。

简单说,它的思想是,每一个异步任务返回一个Promise对象,该对象有一个then方法,允许指定回调函数。比如,f1的回调函数f2,可以写成:

f1().then(f2);

f1要进行如下改写(这里使用的是jQuery的实现):
function f1(){
var dfd = $.Deferred();
setTimeout(function () {
// f1的任务代码
dfd.resolve();
}, 500);
return dfd.promise;
}

这样写的优点在于,回调函数变成了链式写法,程序的流程可以看得很清楚,而且有一整套的配套方法,可以实现许多强大的功能。

比如,指定多个回调函数:

f1().then(f2).then(f3);
再比如,指定发生错误时的回调函数:

f1().then(f2).fail(f3);
而且,它还有一个前面三种方法都没有的好处:如果一个任务已经完成,再添加回调函数,该回调函数会立即执行。所以,你不用担心是否错过了某个事件或信号。这种方法的缺点就是编写和理解,都相对比较难。

Javascript 相关文章推荐
web的各种前端打印方法之jquery打印插件jqprint实现网页打印
Jan 09 Javascript
jQuery实现页面滚动时层智能浮动定位实例探讨
Mar 29 Javascript
纯JavaScript实现获取onclick、onchange等事件的值
Dec 29 Javascript
JavaScript检测弹出窗口是否已经关闭的方法
Mar 24 Javascript
JavaScript通过setTimeout实时显示当前时间的方法
Apr 16 Javascript
javascript HTML+CSS实现经典橙色导航菜单
Feb 16 Javascript
jQuery 获取屏幕高度、宽度的简单实现案例
May 17 Javascript
JavaScript运动框架 解决防抖动问题、悬浮对联(二)
May 17 Javascript
AngularJS实现的输入框字数限制提醒功能示例
Oct 26 Javascript
详解性能更优越的小程序图片懒加载方式
Jul 18 Javascript
JavaScript 自定义html元素鼠标右键菜单功能
Dec 02 Javascript
Node Mongoose用法详解【Mongoose使用、Schema、对象、model文档等】
May 13 Javascript
点击显示指定元素隐藏其他同辈元素的方法
Feb 19 #Javascript
javascript函数重载解决方案分享
Feb 19 #Javascript
一个CSS+jQuery实现的放大缩小动画效果
Feb 19 #Javascript
使用CSS样式position:fixed水平滚动的方法
Feb 19 #Javascript
jQuery拖拽div实现思路
Feb 19 #Javascript
JQuery异步加载无限下拉框级联功能实现示例
Feb 19 #Javascript
调用DOM对象的focus使文本框获得焦点
Feb 19 #Javascript
You might like
PHP截取汉字乱码问题解决方法mb_substr函数的应用
2008/03/30 PHP
php牛逼的面试题分享
2013/01/18 PHP
PHP运行模式的深入理解
2013/06/03 PHP
PHP设计模式入门之迭代器模式原理与实现方法分析
2020/04/26 PHP
IE浏览器兼容Firefox的JS脚本的代码
2008/10/23 Javascript
js 效率组装字符串 StringBuffer
2009/12/23 Javascript
flexigrid 类似ext grid的JS表格代码
2010/07/17 Javascript
js中将HTMLCollection/NodeList/伪数组转换成数组的代码
2011/07/31 Javascript
javascript代码运行不出来执行错误的可能情况整理
2013/10/18 Javascript
jquery动态导航插件dynamicNav用法实例分析
2015/09/06 Javascript
使用javaScript动态加载Js文件和Css文件
2015/10/24 Javascript
Javascript中字符串replace方法的第二个参数探究
2016/12/05 Javascript
Bootstrap CSS组件之大屏幕展播
2016/12/17 Javascript
Bootstrap实现渐变顶部固定自适应导航栏
2020/08/27 Javascript
详解在Node.js中发起HTTP请求的5种方法
2019/01/10 Javascript
webpack4.x下babel的安装、配置及使用详解
2019/03/07 Javascript
javascript实现摄像头拍照预览
2019/09/30 Javascript
mpvue微信小程序开发之实现一个弹幕评论
2019/11/24 Javascript
js实现旋转木马轮播图效果
2020/01/10 Javascript
vue quill editor 使用富文本添加上传音频功能
2020/01/14 Javascript
[01:50]WODOTA制作 DOTA2中文宣传片《HERO》
2013/04/28 DOTA
windows下安装python paramiko模块的代码
2013/02/10 Python
Python中文分词工具之结巴分词用法实例总结【经典案例】
2017/04/15 Python
python文字转语音的实例代码分析
2019/11/12 Python
Tensorflow 1.0之后模型文件、权重数值的读取方式
2020/02/12 Python
CSS代码检查工具stylelint的使用方法详解
2021/03/27 HTML / CSS
环境工程求职简历的自我评价范文
2013/10/24 职场文书
奥利奥广告词
2014/03/20 职场文书
常务副总经理任命书
2014/06/05 职场文书
验房委托书
2014/08/30 职场文书
基层党组织建设整改方案
2014/09/16 职场文书
大一工商管理职业生涯规划:有梦最美,行动相随
2014/09/18 职场文书
2015年个人实习工作总结
2014/12/12 职场文书
好好学习保证书
2015/02/26 职场文书
2016庆祝国庆67周年宣传语
2015/11/25 职场文书
优胜劣汰,强者为王——读《鲁滨逊漂流记》有感
2019/08/15 职场文书