浏览器组成、线程及event loop
浏览器组成
User interface: a. Every part of the browser display, except the window. b. The address bar, back/forward button, bookmarking menu, etc.
Browser Engine: marshals actions between the UI and the rendering engine. This provides a high-level interface to the Rendering Engine. The Browser Engine provides methods to initiate the loading of a URL and other high-level browsing actions (reload, back, forward). The Browser Engine also provides the User interface with various messages relating to error messages and loading progress.
Rendering engine(渲染引擎/内核) : a. Parse HTML and CSS. b. Display parsed content on the screen.
JavaScript interpreter: Used to parse and execute JavaScript code.
浏览器的线程
浏览器是多线程的,它们在内核制控下相互配合以保持同步。一个浏览器至少实现三个常驻线程:JavaScript引擎线程,GUI渲染线程,浏览器事件触发线程。
1) javascript引擎是基于事件驱动单线程执行的(可以修改DOM,简单化处理了),必须符合ECMAScript规范。JS引擎一直等待着event loop中任务的到来,然后加以处理(只有当前函数执行栈执行完毕,才会去任务队列中取任务执行)。浏览器无论什么时候都只有一个JS线程在运行JS程序。
2) UI渲染线程负责渲染浏览器界面,当界面需要重绘(Repaint)或由于某种操作引发回流(reflow)时,该线程就会执行。但是 GUI渲染线程与JS引擎是互斥的,当JS引擎执行时GUI线程会被挂起,JS对页面的操作即GUI的更新也会被保存在一个队列中,等到JS引擎空闲时才有机会被执行。这就是JS阻塞页面加载。
3) 事件触发线程,当一个事件被触发时该线程会把事件添加到任务队列的队尾,等待JS引擎的处理。这些事件可以来自JavaScript引擎当前执行的代码块调用setTimeout/ajax添加一个任务,也可以来自浏览器其他线程如鼠标点击添加的任务。但由于JS的单线程关系,所有这些事件都得排队等待JS引擎处理。
"任务队列"是一个事件的队列(也可以理解成消息队列),当浏览器的其他线程完触发一项任务时,就在js引擎的"任务队列"中添加一个事件/任务,等js引擎的函数执行栈空闲时就会读取任务队列的事件,并执行该事件对应的回调函数。队列中后面的任务必须等前面的任务执行完才能被执行。该过程会一直循环不停,如下图所示:
Event loops
在HTML规范中有要求浏览器实现。简单总结如下:
为了协调事件,用户接口,脚本,渲染,网络等,浏览器必须使用event loops
。有对 browsing contexts和 workers的两种类型。
An event loop has one or more task queues.来自同一个task source的所有tasks必须放入同一个task queue。通常有4中task source:DOM manipulation task source,user interaction task source,networking task source和history traversal task source。同一个task queue是按先进先出顺序执行的。浏览器可以根据task queue中task source的不同,给予task queue不同的优先级。比如为了及时响应,the user agent could then give keyboard and mouse events preference over other tasks three quarters of the time。即由浏览器决定挑哪一个task queue中的队首task执行。
另外Each event loop has a microtask queue. Microtask execute in order, and are executed:
- after every callback, as long as no other JavaScript is mid-execution;
- at the end of each task.
task分为两类:
macro-task:script( js codes as a whole in script tag),setTimeout, setInterval, setImmediate, requestAnimationFrame, I/O, UI rendering;
micro-task:process.nextTick, Promises, Object.observe, MutationObserver.
js引擎会把task push到对应的macrotask queue(即task queue)或microtask queue中。在event loop的一个回合中,会先从macrotask queue中取出队首的任务进行执行;执行完毕后,再依次执行microtask queue中的所有任务;如果在执行过程中添加了新的microtask,则会在当前的回合中继续执行,直到全部mircotask执行完毕才进入下一个event loop回合。
An event loop must continually run through the following steps for as long as it exists:
- select the oldest task(task A) in task queues.If task A is null(means task queues is empty),jump to step 5(microtasks steps)
- set "currently running task" to "task A"
- run "task A"(means run the callback function)
- set "currently running task" back to null,and remove "task A" from its task queue
- perform microtask queue
- (a).select the oldest task(task x) in microtask queue
- (b).if task x is null(means microtask queues is empty),jump to step (g)
- (c).set "currently running task" to "task x"
- (d).run "task x"
- (e).set "currently running task" to null,remove "task x" from the microtask queue
- (f).select next oldest task in microtask queue,jump to step(b)
- (g).finish microtask queue;
- update rendering
- next routnd:jump to step 1
microtask执行时间过长会导致macotask任务的阻塞,导致UI渲染的阻塞。nodejs的process.nextTick限制1000个tick任务,以使macrotask得以执行。
案例解析1:
//promises come from ECMAScript rather than HTML
setTimeout(function(){console.log(4)},0);
new Promise(function(resolve){
console.log(1)
for( var i=0 ; i<10000 ; i++ ){
i==9999 && resolve()
}
console.log(2)
}).then(function(){
console.log(5)
})then(function(){
console.log(6)
});
console.log(3);
//整个js代码是script task,属于macrotask,会在当前task queue中执行,在执行过程中碰到setTimeout,会push到task queue中,但要等下一回合执行;第一个then()属于microtask,在本回合执行,返回undefined,第二个then()也会在当前回合执行
案例解析2:js线程一直执行,stack就不为空,浏览器就没有机会取task queue中的UI render任务更新页面。chrome dev tools的断点调试会影响event loop实时更新UI.
Render queue:浏览器在1s中渲染页面60次,每16ms就会往Render queue中添加一个UI render任务。但是浏览器只有在stack为空时才有机会执行该任务。通过setTimeout(cb,0)将任务分割,就是增加UI render 任务被执行的机会,改善用户体验。
可以将计算量大的任务通过setTimeout分割成多个小任务,这样浏览器就有时间执行UI线程。但是不好的地方是计算所要花的时间会更长。分析performance,想办法改善代码(单次小任务中执行更多的计算)以降低cpu idle的占比。
通过js代码触发click事件(targetEle.click())会导致在执行回调的间隙仍在script task中,故stack不会为空,阻塞microtask执行。
浏览器组成、线程及event loop的更多相关文章
- 进程,线程,Event Loop(事件循环),Web Worker
线程,是程序执行流的最小单位.线程可与同属一个进程的其他线程共享所拥有的全部资源,同一进程中的多个线程之间可以并发执行.线程有就绪,阻塞,运行三种基本状态. 阮一峰大神针对进程和线程的类比,很是形象: ...
- js高级-浏览器事件循环机制Event Loop
JavaScript 是队列的形式一个个执行的 同一时间只能执行一段代码,单线程的 (队列的数据结构) 浏览器是多线程的 JavaScript执行线程负责执行js代码 UI线程负责UI展示的 Jav ...
- 浏览器与Node的事件循环(Event Loop)有何区别?
前言 本文我们将会介绍 JS 实现异步的原理,并且了解了在浏览器和 Node 中 Event Loop 其实是不相同的. 一.线程与进程 1. 概念 我们经常说 JS 是单线程执行的,指的是一个进程里 ...
- 理解Javascript的Event Loop
一.单线程 js作为浏览器脚本语言,他的主要用途是与用户交互,以及操作DOM,这决定了它只能是单线程,为什么呢?因为假如js同时有两个线程,一个线程是在DOM上增加内容,另一个线程是删除这个节点,那么 ...
- js的事件循环(Event Loop)
(本文从掘金小册整理) 首先介绍一下几个概念 进程与线程 相信大家经常会听到 JS 是单线程执行的,但是你是否疑惑过什么是线程? 讲到线程,那么肯定也得说一下进程.本质上来说,两个名词都是 CPU 工 ...
- HTML Standard系列:Event loop、requestIdleCallback 和 requestAnimationFrame
HTML Standard系列:Event loop.requestIdleCallback 和 requestAnimationFrame - 掘金 https://juejin.im/post/5 ...
- 一篇文章图文并茂地带你轻松学完 JavaScript 事件循环机制(event loop)
JavaScript 事件循环机制 (event loop) 本篇文章已经默认你有了基础的 ES6 和 javascript语法 知识. 本篇文章比较细致,如果已经对同步异步,单线程等概念比较熟悉的读 ...
- 解决async 运行多线程时报错RuntimeError: There is no current event loop in thread 'Thread-2'
原来使用: loop = asyncio.get_event_loop()task = asyncio.ensure_future(do_work(checker))loop.run_until_co ...
- 不要在nodejs中阻塞event loop
目录 简介 event loop和worker pool event loop和worker pool中的queue 阻塞event loop event loop的时间复杂度 Event Loop中 ...
随机推荐
- Eclipse安装genymotion最新的方法
https://www.cnblogs.com/WXBai/p/5938884.html 安卓开发: http://tools.android-studio.org/index.php/sdkhttp ...
- Android Studio如何配置adb以及常用命令
https://blog.csdn.net/google_huchun/article/details/53314046 用Android Studio一年多了,都没有使用其调试adb,今天就分享ad ...
- Java中静态变量的声明位置
Java中静态变量只能是成员变量,局部方法中的局部变量除final外不能有任何其他修饰符,例如: public class Test { static String x = "1" ...
- Polycarp Restores Permutation
http://codeforces.com/contest/1141/problem/C一开始没想法暴力的,next_permutation(),TLE 后来看了这篇https://blog.csdn ...
- (二)Audio子系统之new AudioRecord()
在上一篇文章<(一)Audio子系统之AudioRecord.getMinBufferSize>中已经介绍了AudioRecord如何获取最小缓冲区大小,接下来,继续分析AudioReco ...
- jedis 连接redis
一, 单机版连接 @Test public void testJedis() { //1. 创建jedis 对象 Jedis jedis = new Jedis("192.168.88.1 ...
- c# Equals方法
很多C#的教材都会强调对象相等的概念.我们都知道,在C#的世界里存在两种等同性.一种是逻辑等同性:如果两个对象在逻辑上代表同样的值,则称他们具有逻辑等同性.另一种是引用等同性:如果两个引用指向同一个对 ...
- Mac下安装Fiddler抓包工具(别试了,会报错,没办法使用)
下载: https://www.telerik.com/download/fiddler 离线版本:(链接: https://pan.baidu.com/s/1hr7f8QK 密码: ukg2) 安装 ...
- TortoiseGit学习系列之TortoiseGit基本操作拉取项目(图文详解)
前面博客 TortoiseGit学习系列之TortoiseGit基本操作克隆项目(图文详解) TortoiseGit学习系列之TortoiseGit基本操作修改提交项目(图文详解) TortoiseG ...
- 解决SSH连接linux时长时间不操作自动断开
最近重装Linux系统,但是这次ssh连接云服务区Linux系统时,经常出现一段时间不操作,连接自动中断,表现为光标还在闪动,但是却无法操作.只好关闭终端,重新连接,很是麻烦. 为此,通过网络查找,找 ...