单线程的JS引擎

先来思考一个问题,JS 是单线程的么?为什么单线程的JavaScript却能让AJAX异步发送和回调请求,还有setTimeout也看起来像是多线程的?还有non-blocking IO, event loop等概念。

目录:

  1. JS单线程
  2. 浏览器多线程
  3. setTimeout(func, 0) 的应用场景
  4. setTimeout与setInterval
  5. 参考资料
  6. TODO: 接下来,梳理JS的并发模型与 Event Loop

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/EventLoop

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



1. JS 单线程

浏览器无论在什么时候都只有一个线程在运行JavaScript程序。单线程,即在某个特定的时刻只有特定代码能执行,并阻塞其他代码。

而浏览器是事件驱动的(event-driven),可以将事件看做浏览器派给JS引擎的任务,

这些任务可能来自JS引擎当前正在执行的代码块,比如说,调用 setTimeout() 添加一个任务;

或者是来自浏览器内核的其他线程,比如说,界面元素鼠标点击事件、定时器时间到达通知、异步请求状态变更通知(a mouse click, a timer firing, or an XMLHttpRequest completing),

从代码角度看,任务实体就是各种回调函数。因为JS引擎是单线程的,这些任务得排队(加入JS引擎的处理队列),等待被JS引擎执行。

  • 好处:

如果多线程,那么删除或者创建dom元素,都需要在线程之间通信。

所以,单线程简单,不需要考虑线程同步;没有线程切换维护开销,省内存。



2. 浏览器多线程

浏览器内核实现允许多个线程异步执行,这些线程在内核制控下相互配合以保持同步。

浏览器内核可能有如下线程:

常驻线程:

  • 界面渲染线程
  • 事件响应线程

执行完就终止的线程:

  • Http请求线程
  • 定时器线程

一张图说明单线程的JS引擎如何与其他线程通信:

  • JS引擎线程 与 界面渲染线程:互斥

界面渲染线程负责渲染浏览器界面HTML元素,当界面需要重绘(Repaint)或由于某种操作引发回流(reflow)时,该线程就会执行.
JavaScript脚本可操纵DOM元素,如果在修改这些元素属性的同时渲染界面,那么渲染线程前后获得的元素数据就可能不一致了。
在JavaScript引擎运行脚本期间,浏览器渲染线程都是处于挂起状态的,也就是说被”冻结”了.
所以,在脚本中对界面进行更新操作,如添加结点、删除结点或改变结点的外观等更新并不会立即体现出来,这些操作将保存在一个队列中,待JavaScript引擎空闲时才有机会渲染出来.

  • 事件触发线程

用户点击鼠标 -》 被浏览器的事件触发线程捕获 -》 形成一个鼠标点击事件添加到 JS引擎的处理队列 末尾

  • 定时触发线程

定时计数器并不是由JS引擎计数的,因为JS引擎单线程,如果处于线程阻塞状态就记不了时,需要依赖外部计数器。

定时器仅仅只是计划代码在未来的某个时间执行,而执行时机是不能保证的。

它的工作方式是:指定一个时间间隔,表示何时将定时器的代码插入到 JS引擎的处理队列,而不是何时执行代码。其等待的时间基于队列里正在等待的消息数量。

  • ajax 异步请求

浏览器新开一个线程请求,当请求状态改变时,若已设置回调(即 onreadystatechange 中设置的回调函数),该异步线程则将状态变更事件放入 JS引擎的处理队列 末尾等待处理。



3. setTimeout(func, 0) 的应用场景

它告诉js引擎,在0ms以后把func放到主事件队列中,等待当前的代码执行完毕再执行,注意:重点是改变了代码流程,把func的执行放到了等待当前的代码执行完毕再执行。

  • 让浏览器渲染当前的变化(很多浏览器UI render和js执行是放在一个线程中,线程阻塞会导致界面无法更新渲染)【待理解】
  • 重新评估”script is running too long”警告【待理解】
  • 改变执行顺序

下例中,点击按钮就会显示"calculating....",如果删除setTimeout就不会。因为reDraw事件被进入事件队列,到长时间操作的最后才能被执行,所以无法刷新。

 1 <button id=‘do‘> Do long calc!</button>
 2 <div id=‘status‘></div>
 3 <div id=‘result‘></div>
 4
 5
 6 $(‘#do‘).on(‘click‘, function(){
 7
 8   $(‘#status‘).text(‘calculating....‘); //此处会触发redraw事件的fired,但会放到队列里执行,直到long()执行完。
 9
10   // without set timeout, user will never see "calculating...."
11   //long();//执行长时间任务,阻塞
12
13   // with set timeout, works as expected
14   setTimeout(long,50);//用定时器,大约50ms以后执行长时间任务,放入执行队列,但在redraw之后了,根据先进先出原则
15
16  })
17
18
19
20 function long(){
21   var result = 0
22   for (var i = 0; i<1000; i++){
23     for (var j = 0; j<1000; j++){
24       for (var k = 0; k<1000; k++){
25         result = result + i+j+k
26       }
27     }
28   }
29   $(‘#status‘).text(‘calclation done‘) // has to be in here for this example. or else it will ALWAYS run instantly. This is the same as passing it a callback
30 }



4. setTimeout与setInterval

1 setTimeout(function() {
2         /* 代码块... */
3         setTimeout(arguments.callee, 10);
4 }, 10);
5
6 setInterval(function(){
7         /*代码块... */
8 }, 10);

这两段代码看一起效果一样,其实非也,第一段中回调函数内的 setTimeout 是 JS引擎 执行后再设置新的setTimeout定时,假定上一个回调处理完到下一个回调开始处理为一个时间间隔,理论上两个setTimeout回调的执行时间间隔>=10ms。第二段自 setInterval 设置定时后,定时触发线程就会源源不断的每隔十秒产生异步定时事件,并放到任务队列尾,理论上两个setInterval回调执行时间间隔<=10。



5. 参考资料

深入理解JavaScript定时机制

Javascript是单线程的深入分析

How JavaScript Timers Work

JavaScript 高级程序设计(第三版) 22.3 高级定时器

时间: 2024-10-21 11:19:32

单线程的JS引擎的相关文章

js单线程、js任务队列、异步操作

2017.9.22[面试阶段] 一:js由来 JavaScript 1995年首次设计在浏览器上,领导者为了让他看起来更像java,所以起名JavaScript: js是兼容ECMA标准,也称为ECMAScript:js是一种直译式脚本语言,是一种动态类型.弱类型.基于原型的语言: 他的解释器称为JavaScript引擎,为浏览器的一部分,广泛用于客户端的脚本语言,最早用在HTML上,添加动态功能 二:单线程是js的一大特性 不像其他语言如java一样多线程,不用考虑线程同步的问题: js是用户

JS引擎的执行机制

深入理解JS引擎的执行机制 1.灵魂三问 : JS为什么是单线程的? 为什么需要异步? 单线程又是如何实现异步的呢? 2.JS中的event loop(1) 3.JS中的event loop(2) 4.说说setTimeout 首先,请牢记2点: (1) JS是单线程语言 (2) JS的Event Loop是JS的执行机制.深入了解JS的执行,就等于深入了解JS里的event loop 1.灵魂三问 : JS为什么是单线程的? 为什么需要异步? 单线程又是如何实现异步的呢? 技术的出现,都跟现实

从浏览器多进程到JS单线程,JS运行机制最全面的一次梳理(转)

前言 见解有限,如有描述不当之处,请帮忙及时指出,如有错误,会及时修正. ----超长文+多图预警,需要花费不少时间.---- 如果看完本文后,还对进程线程傻傻分不清,不清楚浏览器多进程.浏览器内核多线程.JS单线程.JS运行机制的区别.那么请回复我,一定是我写的还不够清晰,我来改... ----正文开始---- 最近发现有不少介绍JS单线程运行机制的文章,但是发现很多都仅仅是介绍某一部分的知识,而且各个地方的说法还不统一,容易造成困惑. 因此准备梳理这块知识点,结合已有的认知,基于网上的大量参

10分钟理解JS引擎的执行机制

10分钟理解JS引擎的执行机制 javascript 阅读约 7 分钟 深入理解JS引擎的执行机制 1.灵魂三问 : JS为什么是单线程的? 为什么需要异步? 单线程又是如何实现异步的呢? 2.JS中的event loop(1) 3.JS中的event loop(2) 4.说说setTimeout 首先,请牢记2点: (1) JS是单线程语言 (2) JS的Event Loop是JS的执行机制.深入了解JS的执行,就等于深入了解JS里的event loop 1.灵魂三问 : JS为什么是单线程的

浏览器内核、渲染引擎、js引擎

[1]定义 浏览器内核分成两部分渲染引擎和js引擎,由于js引擎越来越独立,内核就倾向于只指渲染引擎 渲染引擎是一种对HTML文档进行解析并将其显示在页面上的工具 [2]常见引擎 渲染引擎: firefox使用gecko引擎 IE使用Trident引擎 2015年微软推出自己新的浏览器,原名叫斯巴达,后改名edge,使用edge引擎 opera最早使用Presto引擎,后来弃用 chrome\safari\opera使用webkit引擎 13年chrome和opera开始使用Blink引擎 js

【转】浏览器内核、渲染引擎、js引擎

[1]定义 浏览器内核分成两部分渲染引擎和js引擎,由于js引擎越来越独立,内核就倾向于只指渲染引擎 渲染引擎是一种对HTML文档进行解析并将其显示在页面上的工具 [2]常见引擎 渲染引擎(内核): firefox使用gecko引擎 IE使用Trident引擎 2015年微软推出自己新的浏览器,原名叫斯巴达,后改名edge,使用edge引擎 opera最早使用Presto引擎,后来弃用 chrome\safari\opera使用webkit引擎 13年chrome和opera开始使用Blink引

【repost】浏览器内核、渲染引擎、js引擎

[1]定义 浏览器内核分成两部分渲染引擎和js引擎,由于js引擎越来越独立,内核就倾向于只指渲染引擎 渲染引擎是一种对HTML文档进行解析并将其显示在页面上的工具 [2]常见引擎 渲染引擎: firefox使用gecko引擎 IE使用Trident引擎 2015年微软推出自己新的浏览器,原名叫斯巴达,后改名edge,使用edge引擎 opera最早使用Presto引擎,后来弃用 chrome\safari\opera使用webkit引擎 13年chrome和opera开始使用Blink引擎 js

java调用javascript :js引擎rhino

java调用javascript :js引擎rhino JavaJavaScriptEclipse脚本SQL 前段时间,在浏览javaeye论坛,看见有人征集如何在java中运行数学表达式.结果方案五花八门:1.jakarta commons JEXL. 2.Beanshell3.Java Math Expression Parser jep 4.parse combinator jparsec 5.jdk 6.0的 script6.利用SQL 7.自己写语法分析如果自己写语法分析,没有个200

JS引擎

在javax.script包下提供有关脚本引擎的类和接口,为我们定义了一系列的规范,达到可以在java应用程序中执行脚本语言编写的程序. javax.script包的主要功能有: 1.脚本执行:执行脚本文件中的程序,使用ScriptEngine的eval方法和Invocable接口的方法. 2.绑定:将java对象作为指定变量公开给脚本程序.相当于为脚本设置全局变量之类的.Bingdings和ScriptContext类用于此目的. 3.编译:将脚本执行一次,生成中间代码,往后只需调用即可,实现