浏览器组成

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:

  1. 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)
  2. set "currently running task" to "task A"
  3. run "task A"(means run the callback function)
  4. set "currently running task" back to null,and remove "task A" from its task queue
  5. 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;
  6. update rendering
  7. 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的占比。

事件冒泡再次执行的回调仍属于同一个task:

通过js代码触发click事件(targetEle.click())会导致在执行回调的间隙仍在script task中,故stack不会为空,阻塞microtask执行。

浏览器组成、线程及event loop的更多相关文章

  1. 进程,线程,Event Loop(事件循环),Web Worker

    线程,是程序执行流的最小单位.线程可与同属一个进程的其他线程共享所拥有的全部资源,同一进程中的多个线程之间可以并发执行.线程有就绪,阻塞,运行三种基本状态. 阮一峰大神针对进程和线程的类比,很是形象: ...

  2. js高级-浏览器事件循环机制Event Loop

    JavaScript 是队列的形式一个个执行的 同一时间只能执行一段代码,单线程的  (队列的数据结构) 浏览器是多线程的 JavaScript执行线程负责执行js代码 UI线程负责UI展示的 Jav ...

  3. 浏览器与Node的事件循环(Event Loop)有何区别?

    前言 本文我们将会介绍 JS 实现异步的原理,并且了解了在浏览器和 Node 中 Event Loop 其实是不相同的. 一.线程与进程 1. 概念 我们经常说 JS 是单线程执行的,指的是一个进程里 ...

  4. 理解Javascript的Event Loop

    一.单线程 js作为浏览器脚本语言,他的主要用途是与用户交互,以及操作DOM,这决定了它只能是单线程,为什么呢?因为假如js同时有两个线程,一个线程是在DOM上增加内容,另一个线程是删除这个节点,那么 ...

  5. js的事件循环(Event Loop)

    (本文从掘金小册整理) 首先介绍一下几个概念 进程与线程 相信大家经常会听到 JS 是单线程执行的,但是你是否疑惑过什么是线程? 讲到线程,那么肯定也得说一下进程.本质上来说,两个名词都是 CPU 工 ...

  6. HTML Standard系列:Event loop、requestIdleCallback 和 requestAnimationFrame

    HTML Standard系列:Event loop.requestIdleCallback 和 requestAnimationFrame - 掘金 https://juejin.im/post/5 ...

  7. 一篇文章图文并茂地带你轻松学完 JavaScript 事件循环机制(event loop)

    JavaScript 事件循环机制 (event loop) 本篇文章已经默认你有了基础的 ES6 和 javascript语法 知识. 本篇文章比较细致,如果已经对同步异步,单线程等概念比较熟悉的读 ...

  8. 解决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 ...

  9. 不要在nodejs中阻塞event loop

    目录 简介 event loop和worker pool event loop和worker pool中的queue 阻塞event loop event loop的时间复杂度 Event Loop中 ...

随机推荐

  1. Centos7 自定义systemctl服务脚本

    序言篇: 之前工作环境一直使用Centos6版本,脚本一直在使用/etc/init.d/xxx:系统升级到Cento7后,虽然之前的启动脚本也可以使用,但一直没有使用systemctl 的自定义脚本. ...

  2. shell ssh远程执行命令

    [root@backup shell]# vi backup.sh #!/bin/sh ipAddress=172.17.167.38 ssh -tt root@$ipAddress -p 22 &l ...

  3. IDEA External libraries 不显示Maven中引入的repository

    原文:https://blog.csdn.net/dj_dengjian/article/details/88668012 记录一下遇到的这个问题的解决方法,也是困惑了半天,感觉这是maven的bug ...

  4. Vijos 小胖的奇偶

    第一遍做 #include<iostream> #include<cstdlib> #include<cstdio> #include<cstring> ...

  5. 达人篇:6.3)试验设计DOE,Design of Experiments

    本章目的:了解DOE,结构工程师为什么学习DOE. 1.前言:结构工程师为什么要学DOE 作者作为一名结构工程师,为什么要学习DOE. 很简单,在第四版FMEA手册中,DOE是重要的探测控制手段.如图 ...

  6. vsftpd文件虚拟用户搭建

    关于vsftpd的原理这里就不多说了,下面红色部分有单独标出,突出显示,意思是这里的东西有额外的配置,全文的配置一定要跟着第二步的配置来,不要过程中随便改变参数,除非你看得懂,好了直接上配置过程 1. ...

  7. 04-树5 Root of AVL Tree (25 分)

    An AVL tree is a self-balancing binary search tree. In an AVL tree, the heights of the two child sub ...

  8. SpringMVC初写(四)上传和下载功能的实现

    一.文件上传 流程: 导入包commons-fileuplad组件和依赖包commons-io组件 配置springmvc支持上传的组件: 启动SpringMVC注解支持 配置上传解释器 构建一个上传 ...

  9. Android四种跨进程通信

    由于android系统中应用程序之间不能共享内存.因此,在不同应用程序之间交互数据(跨进程通讯)就稍微麻烦一些.在android SDK中提供了4种用于跨进程通讯的方式.这4种方式正好对应于andro ...

  10. TCP网络参数优化

    TCP连接的状态 TCP连接过程是状态的转换,促使状态发生转换的因素包括用户调用.特定数据包以及超时等,具体状态如下所示: CLOSED:初始状态,表示没有任何连接. LISTEN:Server端的某 ...