原文:http://www.ruanyifeng.com/blog/2014/10/event-loop.html

为什么JavaScript是单线程

JavaScript语言的一大特点就是单线程,也就是说,同一个时间只能做一件事。那么,为什么JavaScript不能有多个线程呢?

JavaScript的单线程,与它的用途有关。作为浏览器脚本语言,JavaScript的主要用途是与用户互动,以及操作DOM。这决定了它只能是单线程,否则会带来很复杂的同步问题。比如,假定JavaScript同时有两个线程,一个线程再某个DOM节点上添加内容,另一个线程删除了这个节点,这时浏览器应该以哪个线程为准?

所以,为了避免复杂性,从一诞生,JavaScript就是单线程,这已经成了这门语言的核心特征,将来也不会改变。

为了利用多核CPU的计算能力,HTML5提出Web Worker标准,运行JavaScript脚本创建多个线程,但是子线程完全受主线程控制,且不得操作DOM。所以,这个新标准并没有改变JavaScript单线程的本质。

任务队列

单线程就意味着,所有任务需要排队,前一个任务结束,才会执行后一个任务。如果前一个任务耗时很长,后一个任务就不得不一直等着。多线程可以将任务放到不同的线程中去处理。CPU的调度单位是线程,它会在不同的线程之间切换,任务是隶属于线程的。

如果一个线程中,任务排队是因为计算量大,CPU忙不过来,倒也算了。但是很多时候CPU处理一个线程时是闲着的,因为IO设备(输入输出设备)很慢(比如Ajax操作从网络读取数据),不得不等结果出来,任务才能往下执行。

JavaScript语言的设计者意识到,这时主线程完全可以不管IO设备,挂起处于等待中的任务,先运行排在后面的任务。等到IO设备返回了结果,再回头,把挂起的任务继续执行下去。

于是,所有任务可以分成两种,一种是同步任务,另一种是异步任务。同步任务指的是,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;异步任务指的是,不进入主线程、而进入“任务队列”的任务,只有“任务队列”通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。

注:在理解上面的内容时,不要将CPU处理其他进程(比如QQ等)考虑进来,上面内容是针对CPU处理一个线程来说的。在一个单线程中(比如一个页面加载过程中的js处理),会包含许多任务。在任务很多的情况下,想要不被阻塞,采用的方案是异步任务+事件轮询。多线程是将任务分到不同的线程中去,各个线程内部采用的是同步任务。

事件和回调函数

“任务队列”是事件队列(也可以理解成消息队列),表示相关的异步任务可以进入“执行栈”了。主线程读取“任务队列”,这个过程是循环不断的。

“任务队列”中的事件,除了IO设备的事件以外,还包括一些用户产生的事件(比如鼠标点击、页面滚动等等)。只要指定过回调函数,这些事件发生时就会进入“任务队列”,等待主线程读取。

异步任务必须指定回调函数,当主线程开始执行异步任务,就是执行对应的回调函数。

Event Loop

主线程从“任务队列”中读取事件,这个过程是循环不断的,所以整个的这种运行机制又称为Event Loop(事件轮询)。

上图中,主线程运行的时候,产生堆(heap)和栈(stack)。只要栈中的代码执行完毕,主线程就会去读取“任务队列”,依次执行那些事件所对应的回调函数。

任务队列中的任务,是“回调函数”指定的,然后通过事件触发添加进去的,是一种异步任务。而在页面初始化时,主线程执行的任务是同步任务。

需要注意的是,setTimeout()只是将事件插入了"任务队列",必须等到当前代码(执行栈)执行完,主线程才会去执行它指定的回调函数。要是当前代码耗时很长,有可能要等很久,所以并没有办法保证,回调函数一定会在setTimeout()指定的时间执行。

Node.js的Event Loop

Node.js也是单线程的Event Loop,但是它的运行机制不同于浏览器环境。

根据上图,Node.js的运行机制如下。

  1. V8引擎解析JavaScript脚本
  2. 解析后的代码,调用Node API
  3. libuv库负责Node API的执行。它将不同的任务分配给不同的线程,形成一个Event Loop,以异步的方式将任务的执行结果返回给V8引擎。
  4. V8引擎再将结果返回给用户

除了setTimeout和setInterval这两个方法,Node.js还提供了另外两个与“任务队列”有关的方法:process.nextTick和setImmediate。它们可以帮助我们加深对“任务队列”的理解。

process.nextTick方法可以在当前“执行栈”的尾部,下一次Event Loop(主线程读取“任务队列”)之前,触发回调函数。也就是说,它指定的任务总是发生在所有一般任务之前。

setImmediate方法则是在当前“任务队列”的尾部添加事件,也就是说,它指定的任务总是在下一次Event Loop时执行,这与setTimeout很像。

理解JavaScript中的事件轮询的更多相关文章

  1. 理解Nodejs中的事件轮询机制

    我在看<了不起的Nodejs>一书,阻塞与非阻塞IO那一章我来回看了N遍,然后...还是没太看懂..于是我找到了这篇日志,写的是真的有点好啊..潸然泪下.. 原文:http://www.r ...

  2. 理解Node.js的事件轮询

    前言 总括 : 原文地址:理解Node.js的事件轮询 Node小应用:Node-sample 智者阅读群书,亦阅历人生 正文 Node.js的两个基本概念 Node.js的第一个基本概念就是I/O操 ...

  3. node.js中的事件轮询Event Loop

    任务队列/事件队列 "任务队列"是一个事件的队列,IO设备完成一项任务,就在"任务队列"中添加一个事件,表示相关的异步任务可以进入"执行栈" ...

  4. 深入理解javascript中的事件循环event-loop

    前面的话 本文将详细介绍javascript中的事件循环event-loop 线程 javascript是单线程的语言,也就是说,同一个时间只能做一件事.而这个单线程的特性,与它的用途有关,作为浏览器 ...

  5. 理解Javascript中的事件绑定与事件委托

    最近在深入实践js中,遇到了一些问题,比如我需要为动态创建的DOM元素绑定事件,那么普通的事件绑定就不行了,于是通过上网查资料了解到事件委托,因此想总结一下js中的事件绑定与事件委托. 事件绑定   ...

  6. 再次理解javascript中的事件

    一.事件流的概念 + 事件流描述的是从页面中接收事件的顺序. 二.事件捕获和事件冒泡 +    事件冒泡接收事件的顺序:

  7. 理解JavaScript中的事件流

    原文地址:http://my.oschina.net/sevenhdu/blog/332014 目录[-] 事件冒泡 事件捕获 DOM事件流 当浏览器发展到第四代时(IE4和Netscape Comm ...

  8. 理解JavaScript中的事件路由冒泡过程及委托代理机制

    当我用纯CSS实现这个以后.我开始用JavaScript和样式类来完善功能. 然后,我有一些想法,我想使用Delegated Events (事件委托)但是我不想有任何依赖,插入任何库,包括jQuer ...

  9. 理解javascript中的事件模型

    javascript中有两种事件模型:DOM0,DOM2.而对于这两种的时间模型,我一直不是非常的清楚,现在通过网上查阅资料终于明白了一些. 一.  DOM0级事件模型 DOM0级事件模型是早期的事件 ...

随机推荐

  1. hdu 1532(最大流)

    Drainage Ditches Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) ...

  2. 弄清UTF8和Unicode

    长期以来,一直对字符串编码认识比较粗略,认为支持"特殊字符"编码就是Unicode.当然,.NET平台上很少需要考虑这类问题,但搞清一些基本概念还是很有好处的. Unicode这个 ...

  3. DateFomrat Mask in ASPxTextbox increases width ,How to disable display of dxeErrorCell on ASPxTextBox

    让ASPxTextbox显示yyyy-MM-dd, 需设置 <MaskSettings Mask="yyyy-MM-dd" />.但是会导致ASPxTextbox宽度变 ...

  4. Apache +Tomcat的负载均衡与集群配置

    实验拓扑图: 一.搭配环境 (1).Tomcat的安装和配置 Tomcat_a的ip:192.168.55.229 Tomcat_b的ip:192.168.55.231 Tomcat的需要安装jdk和 ...

  5. SharePoint 2016 Beta 2 使用体验

    博客地址:http://blog.csdn.net/FoxDave 上一篇主要描述了安装SharePoint 2016的过程,本篇写一些概览性的东西. 首先打开管理中心(依然是在安装完会有Issue ...

  6. Hadoop随笔(一):工作流程的源码

    一.几个可能会用到的属性值 1.mapred.map.tasks.speculative.execution和mapred.reduce.tasks.speculative.execution 这两个 ...

  7. springmvc学习第四天

    数据类型的转换.格式化.校验 1.数据绑定流程 1. Spring MVC 主框架将 ServletRequest 对象及目标方法的入参实例传递给 WebDataBinderFactory 实例,以创 ...

  8. java堆、栈、堆栈的区别

    1.栈(stack)与堆(heap)都是Java用来在Ram中存放数据的地方.与C++不同,Java自动管理栈和堆,程序员不能直接地设置栈或堆. 2. 栈的优势是,存取速度比堆要快,仅次于直接位于CP ...

  9. Redis持久化-数据丢失及解决(转载)

    本文转载自        Redis持久化-数据丢失及解决  感谢原作者 Redis的数据回写机制 Redis的数据回写机制分同步和异步两种, 同步回写即SAVE命令,主进程直接向磁盘回写数据.在数据 ...

  10. 使用查询(SQ01、SQ02、SQ03)创建报表

    查询需求说明 通过Query(SQ01.SQ02.SQ03)实现根据销售组织查询销售订单中的各项信息,包括物料.金额.成本,以及毛利. 其中对销售组织进行权限检查(Authority Check),字 ...