热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

JavaScript异步编程

简介JavaScript是一种单线程执行的脚本语言,为了不让一段JavaScript代码执行时间过久,阻塞UI的渲染或者是鼠标事件处理,通常会采用一种异步的编程模式。这里就跟大家一起了解一下JavaScript的异步编程模式。一...SyntaxHighlighter.all()

简介

Javascript是一种单线程执行的脚本语言,为了不让一段Javascript代码执行时间过久,阻塞UI的渲染或者是鼠标事件处理,通常会采用一种异步的编程模式。这里就跟大家一起了解一下Javascript的异步编程模式。

 

一、Javascript的异步编程模式

1.1 为什么要异步编程

一开始就说过,Javascript是一种单线程执行的脚本语言(这可能是由于历史原因或为了简单而采取的设计)。它的单线程表现在任何一个函数都要从头到尾执行完毕之后,才会执行另一个函数,界面的更新、鼠标事件的处理、计时器(setTimeout、setInterval等)的执行也需要先排队,后串行执行。假如有一段Javascript从头到尾执行时间比较长,那么在执行期间任何UI更新都会被阻塞,界面事件处理也会停止响应。这种情况下就需要异步编程模式,目的就是把代码的运行打散或者让IO调用(例如AJAX)在后台运行,让界面更新和事件处理能够及时地运行。

下面是一个同步与异步执行的例子
01
 id="output">


02
 
03
 Onclick="updateSync
 ()">Run
 Sync
04
 
05
 Onclick="updateAsync
 ()">Run
 Async
06
 
07


点击"Run Sync"按钮会调用updateSync的同步函数,逻辑非常简单,循环体内每次更新output结点的内容为i。如果在其他多线程模型下的语言,你可能会看到界面上以非常快的速度显示从0到999后停止。但是在Javascript中,你会感觉按钮按下去的时候卡了一下,然后看到一个最终结果999,而没有中间过程,这就是因为在updateSync函数运行过程中UI更新被阻塞,只有当它结束退出后才会更新UI。如果你让这个函数的运行时间增加一下(例如把上限改为1 000 000),你会看到更明显的停顿,在停顿期间点击另一个按钮是没有任何反应的,只有结束之后才会处理另一个按钮的点击事件。

另一个按钮"Run Async"会调用updateAsync函数,它是一个异步函数,乍一看逻辑比较复杂,函数里先声明了一个局部变量i和嵌套函数updateLater(关于内嵌函数的介绍请看Javascript世界的一等公民-函数),然后调用了updateLater,在这个函数中先是更新output结点的内容为i,然后通过setTimeout让updateLater函数异步执行。这个函数的运行后,你会看到UI界面上从0到999快速地更新过程,这就是异步执行的结果。

可见,在Javascript中异步编程甚至是一种必要的编程模式。

 

1.2 异步编程的优缺点

异步编程的优点是显而易见的,异步编程你可以实现前面例子中一边运行一边更新的效果;或是利用异步IO让UI运行更加流畅,比如通过XMLHTTPRequest的异步接口获取网络数据,在获取完成后再更新界面,在异步获取数据的时候不会阻碍UI的更新。在众多HTML5设备API的设计中都充分采用了异步编程模式,例如W3C的File System API、File API、Indexed Database API,Windows 8 API,PhoneGap API,服务端脚本Node JS API等等。

异步编程也有一些缺点,造成深度嵌套的函数调用,破坏了原有的简单逻辑,让代码难以读懂。

 

二、异步编程接口设计

 

2.1 W3C原生接口

W3C原生接口的设计经常采用回调函数和事件触发形式,前者在调用异步函数时直接传入回调函数作为参数,后者在原始对象上绑定事件处理函数,异步函数出错时一般不会抛出异常,而是通过调用错误回调函数或触发错误事件。从语义上看,回调函数形式是为了获取某一个函数的运行结果,而事件触发形式通常会用于表示某些状态变化(加载、出错、进度变化、收到消息等等)。个人或团队开发小型项目时可以参考这两种形式的接口设计。

 

回调函数:例如W3C的File System API中,在请求虚拟文件系统实例、读写文件等接口中,都采用了回调函数的形式:

01
requestFileSystem(TEMPORARY,
 1024 * 1024, function(fs)
 {
02
 
03
         //
 异步获取虚拟文件系统实例fs
04
 
05
fs.root.getFile("already_there.txt", null, function (f)
 {
06
 
07
         //
 获取文件already_there.txt
08
 
09
             getAsText(f.file());
10
 
11
}, function(err)
 {
12
 
13
         //
 获取文件出错
14
 
15
});
16
 
17
}, function(err)
 {
18
 
19
         //
 获取虚拟文件系统失败
20
 
21
});
 

事件触发:例如W3C的XMLHTTPRequest(AJAX)就是一种通过事件触发这种形式实现,当AJAX请求成功或失败时触发onload、onerror事件:

01
var xhr
 = new XMLHTTPRequest();
02
 
03
xhr.onload
 = function()
 {
04
 
05
         //
 加载成功时触发onload事件
06
 
07
};
08
 
09
xhr.onerror
 = function()
 {
10
 
11
         //
 加载失败时触发onerror事件
12
 
13
};
14
 
15
xhr.open(‘GET',
 ‘/get-ajax', true);
16
 
17
xhr.send(null);
 

2.2 第三方异步接口设计

采用回调函数形式的接口写代码,会带来比较严重的函数嵌套问题,就像著名的LISP一样,引入大量有争议性的括号,让本来是前后顺序执行的代码段形式上变成了一层套一层的结构,影响了Javascript代码逻辑的清晰性。解决这个问题,要让逻辑上的先后顺序执行的代码,在形式上也是顺序的,而不是嵌套的,这就需要更好的异步接口设计方案。

CommonJS是一个著名的Javascript的开源组织,目标是设计与JS环境无关的标准接口,并提供像Ruby、Python类似的标准库函数。在CommonJS中有三个异步编程模式相关的接口提案:Promises/A、Promises/B和Promises/D。Promise,中文意思为承诺,意思就是说承诺完成一个任务,在完成时告之是否执行成功,并返回结果。

这里我们只介绍最简单的异步接口Promises/A,在使用这种接口的函数时,函数的返回值是一个Promise对象,它有三种状态:不满足条件(unfulfilled)、满足条件(fulfilled)、失败(failed),顾名思义不满足条件状态就是异步函数刚刚调用,尚未真正执行时的状态,满足条件就是执行成功时的状态,失败就是执行失败的状态。它的接口函数也只有一个:

then(fulfilledHandler, errorHandler, progressHandler)

这三个参数分别是满足条件、失败以及进度有变化时的回调函数,他们的参数分别对应异步调用的结果,而then的返回值仍然是一个Promise对象,这个对象包含了上一步异步调用回调函数的返回值,因此可以链式地写下去,表现上成为顺序执行的逻辑。例如,假如W3C的File System API采用Promises/A的接口设计,2.1节的例子可以写作:

01
requestFileSystem(TEMPORARY,
 1024 * 1024)
02
 
03
.then(function(fs)
 {
04
 
05
         //
 异步获取虚拟文件系统实例fs
06
 
07
         return fs.root.getFile("already_there.txt", null);
08
 
09
})
10
 
11
.then(function(f)
 {
12
 
13
//
 获取文件already_there.txt
14
 
15
    getAsText(f.file());
16
 
17
});


看是不是清楚多了?

实现Promises/A接口的JS库有很多,比如when.js、node-promise、promised-io等,微软的Windows 8 Metro应用的接口设计也采用了相同的接口设计,详见Asynchronious Programming in Javascript with "Promises"。

 

2.3 异步同步化

第三方的异步接口一定程度上解决了代码逻辑与执行顺序不一致的问题,但是仍然有些情况下,让代码难以读懂。我们还以1.1节中的代码为例,updateAsync即使采用Promises API并不会更好理解,而代码实现的功能其实就是一个很简单的循环+更新的功能。这时候就需要一些异步同步化来帮助实现。

所谓异步同步化顾名思义就是采用同步形式的语法实现异步调用。这里简单地介绍一下老赵的Jscex,它是一个纯Javascript实现的库,可以在任何浏览器JavaScript环境中运行,不仅支持异步同步化的编程语法,还支持并行执行等特性。用Jscex来重写1.1节中的代码,将是这样
01
function updateAsync()
 {
02
    var update
 = eval(Jscex.compile('async', function()
 {
03
 
04
        for (var i
 = 0; i <1000; i++) {
05
            document.getElementById(&#39;output&#39;).innerHTML
 = i;
06
            $await(Jscex.Async.sleep(0));  //
 sleep 0 ms to make it asynchronous
07
        }
08
 
09
    }));
10
    
11
    update().start();
12
}


其中update是用Jscex编译生成的函数,它会返回一个Jscex的Task对象,通过调用它的start方法来执行这个Task。Update函数的逻辑跟updateSync几乎一样,$await是Jscex增加的关键字,用于等待一个异步任务的调用结果,Jscex.Async.sleep是Jscex内建的一个异步任务,用于显式地等待几毫秒,加入这行语句之后会被Jscex编译器生成异步的代码,实现一边计算一边更新UI的效果,代码结构保持简洁清楚。

 


推荐阅读
  • 1、概述首先和大家一起回顾一下Java消息服务,在我之前的博客《Java消息队列-JMS概述》中,我为大家分析了:然后在另一篇博客《Java消息队列-ActiveMq实战》中 ... [详细]
  • YOLOv7基于自己的数据集从零构建模型完整训练、推理计算超详细教程
    本文介绍了关于人工智能、神经网络和深度学习的知识点,并提供了YOLOv7基于自己的数据集从零构建模型完整训练、推理计算的详细教程。文章还提到了郑州最低生活保障的话题。对于从事目标检测任务的人来说,YOLO是一个熟悉的模型。文章还提到了yolov4和yolov6的相关内容,以及选择模型的优化思路。 ... [详细]
  • 本文讨论了Alink回归预测的不完善问题,指出目前主要针对Python做案例,对其他语言支持不足。同时介绍了pom.xml文件的基本结构和使用方法,以及Maven的相关知识。最后,对Alink回归预测的未来发展提出了期待。 ... [详细]
  • Mono为何能跨平台
    概念JIT编译(JITcompilation),运行时需要代码时,将Microsoft中间语言(MSIL)转换为机器码的编译。CLR(CommonLa ... [详细]
  •     这里使用自己编译的hadoop-2.7.0版本部署在windows上,记得几年前,部署hadoop需要借助于cygwin,还需要开启ssh服务,最近发现,原来不需要借助cy ... [详细]
  • 与.Net大师Jeffrey Richter面对面交流——TUP对话大师系列活动回顾(多图配详细文字)...
    与.Net大师JeffreyRichter面对面交流——TUP对话大师系列活动回顾(多图配文字)上周末很有幸参加了CSDN举行的TUP活动, ... [详细]
  • ruby 输出彩色内容到控制台
    程序输出控制台时,为了区分输出信息的严重程度,可以使用颜色、符号等来做标识。ruby也支持设置输出内容的颜色,比如运行以下代码:以下内容是百度到的,因发现很多博客都是同样的写法,所 ... [详细]
  • 用JavaScript实现的太空人手表
    用JavaScript实现的太空人手表-JS写的太空人手表,没有用canvas、svg。主要用几个大的函数来动态显示时间、天气这些。天气的获取用到了AJAX请求。代码中有详细的注释 ... [详细]
  • 本文介绍了lua语言中闭包的特性及其在模式匹配、日期处理、编译和模块化等方面的应用。lua中的闭包是严格遵循词法定界的第一类值,函数可以作为变量自由传递,也可以作为参数传递给其他函数。这些特性使得lua语言具有极大的灵活性,为程序开发带来了便利。 ... [详细]
  • Java实战之电影在线观看系统的实现
    本文介绍了Java实战之电影在线观看系统的实现过程。首先对项目进行了简述,然后展示了系统的效果图。接着介绍了系统的核心代码,包括后台用户管理控制器、电影管理控制器和前台电影控制器。最后对项目的环境配置和使用的技术进行了说明,包括JSP、Spring、SpringMVC、MyBatis、html、css、JavaScript、JQuery、Ajax、layui和maven等。 ... [详细]
  • 本文介绍了使用cacti监控mssql 2005运行资源情况的操作步骤,包括安装必要的工具和驱动,测试mssql的连接,配置监控脚本等。通过php连接mssql来获取SQL 2005性能计算器的值,实现对mssql的监控。详细的操作步骤和代码请参考附件。 ... [详细]
  • 本文介绍了在Mac上安装Xamarin并使用Windows上的VS开发iOS app的方法,包括所需的安装环境和软件,以及使用Xamarin.iOS进行开发的步骤。通过这种方法,即使没有Mac或者安装苹果系统,程序员们也能轻松开发iOS app。 ... [详细]
  • 表单代码 ... [详细]
  • 招聘 | 涂鸦智能招聘IoT安全人才
    招聘 | 涂鸦智能招聘IoT安全人才 ... [详细]
  • 作为老牌的Web后端编程语言,PHP在全球市场占有率非常高。PHP是全球五大热门的编程语言,是唯一入选的脚本语言;从各个招聘网站的数据上来 ... [详细]
author-avatar
多米音乐_34026248
这个家伙很懒,什么也没留下!
Tags | 热门标签
RankList | 热门文章
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有