浏览器组成

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. 课堂练习:ex 4-20

    一.习题要求 • 定义一个复数类Complex. • 有相加,输出,模计算函数. • 模计算要求结果保存在第一个复数中. 二.习题内容 //complex.h # ifndef COMPLEX_H # ...

  2. windows jenkins 卸载

      如果下载的是war包,先在任务管理器上停止jenkins的服务,再删除jenkins整个文件

  3. springboot第四篇:debug模式开发运用

    前提:项目是以maven project结构建立的,现状是无法进行断点调试的.怎么才能在eclipse里进行调试呢? 需要:①将项目打包部署到tomcat ②往项目加入dynamic web modu ...

  4. pycharm+gitee

    Git操作 前言: 由于各种原因,很多时候我们写代码的电脑并不会随身携带,所以有的时候突发灵感想继续写代码就变得难以实现.相信大部分同学对此都有了解,那就通过代码托管平台来管理.原本想用GitHub来 ...

  5. 洛谷P3980 [NOI2008]志愿者招募

    题解 最小费用最大流 每一天是一条边\((inf-a[i], 0)\) 然后对于一类志愿者, 区间两端连一条\((inf, c[i])\) \(S\)向第一个点连\((inf, 0)\) 最后一个点向 ...

  6. MYSQL 缓存

    在PHP.INI中query_cache_type设置为1. 即 开始MYSQL全局SQL语句 都缓存.(0 不使用) 临时关闭查询缓冲的方法:1.    SELECT SQL_NO_CACHE fi ...

  7. 【数组】word search

    题目: Given a 2D board and a word, find if the word exists in the grid. The word can be constructed fr ...

  8. Cloudera Manager安装之Cloudera Manager安装前准备(CentOS6.5)(一)

    Cloudera Manager安装前准备 (一)机器准备 192.168.80.148   clouderamanager01 (部署ClouderaManager-server和Mirror se ...

  9. vue引入bootstrap和fontawesome

    npm install jquery npm install bootstrap npm install popper.js. import $ from 'jquery' import 'boots ...

  10. 一头扎进 Java IO中

    Java IO 概述 在这一小节,我会试着给出Java IO(java.io)包下所有类的概述.更具体地说,我会根据类的用途对类进行分组.这个分组将会使你在未来的工作中,进行类的用途判定时,或者是为某 ...