渲染进程里的多个线程

7/19/2022 浏览器

# 0.引子

一个 Tab 页面里的所有执行任务都是在渲染进程(render process)进行的,其中包括:页面渲染、JS 执行、事件循环、异步 Http 请求等。请牢记,浏览器的渲染进程是多线程的

下面来看看主要包含的线程

# 1.GUI 渲染线程

GUI 即 Graphical User Interface,图形用户界面。该线程是由渲染引擎(render engine)驱动的,主要任务如下:

  • 渲染页面,包括解析 HTML、CSS,构建渲染树(render tree),布局和绘制等

  • 执行重排/回流(Layout/Reflow)和重绘(Repaint)

详情可以参考同专栏的《浏览器渲染原理 (opens new window)

注意:GUI渲染线程与JS引擎线程互斥,JS 引擎线程执行时,GUI 渲染线程会被挂起,GUI 更新会被推入到队列中等JS引擎空闲时后执行

# 2.JS 引擎线程

顾名思义,就是由JS引擎(javaScript engine)驱动,执行 JS 代码的线程,主要任务如下:

  • 解析 JS 代码
  • 执行 JS 代码

注意:JS 是单线程的,且与 GUI 渲染线程互斥,意味着当 JS 执行过长,可能会造成页面渲染不连贯,会造成堵塞

# 3.事件触发线程

JS 是单线程的,不能独立完成异步操作,故事件触发线程用于辅助 JS 引擎实现事件监听异步回调操作,主要任务如下:

  • 当 JS 引擎执行setTimeoutajax请求等异步操作时等触发了事件(即拿到了结果后)会将对应回调任务添加到自身维护的任务队列
  • 等待 JS 引擎空闲时,会访问事件触发线程中的任务队列,从中取出任务按入队先后顺序执行

补充:

  • 异步的宏任务例如 setTimeout、ajax 维护在事件触发线程任务队列
  • 微任务维护在JS引擎线程任务队列
  • 在执行完同步的宏任务后,会先从 JS 引擎线程的任务队列中拿到微任务逐一执行完毕然后才从事件触发线程中的任务队列取出异步宏任务,故执行顺序永远是宏=>微=>宏,这里的宏和微并非单纯的,可能一个宏任务里面包含了微任务,这个微任务又包含宏任务和下一个微任务

# 4.定时触发器线程

setTimeoutsetTnterval所在线程,负责具体的计时任务

# 5.异步 Http 请求线程

在 JS 代码中使用 ajax 发送 http 请求时,会开启这个线程,负责具体的 http 请求任务

# 6.JS 引擎线程与其他线程的协作(总结)

不多 bb,直接来看代码

// setTimeout
setTimeout(() => {
	alert(1)
}, 1000)

// 1.这里代码由js引擎线程执行
// 2.计数1000ms这个任务交由定时触发器线程去完成,然后js引擎就暂时不管,执行其他后面的代码
// 3.等1000ms后,定时器触发器完成任务,通知事件触发线程将回调` ()=>{alert(1)} `推入到任务
// 队列中,待js引擎空闲时取出执行
// 异步ajax请求
let xhr = new XMLHttpRequest()
xhr.onreadystatechange = () => {
	xhr.onload = () => {
		// a
		alert(xhr.responseText)
	}
}
xhr.open('get', 'https://www.some.com/api', true) // b
xhr.send(null) // b

// 1.JS引擎线程执行完b开启异步http请求线程,然后暂时不管了,执行后面的代码
// 2.当异步http请求线程完成网络请求并返回数据了,会通知事件触发线程将a回调
// ` ()=>alert(xhr.responseText) `推入任务队列,等js引擎空闲了取出执行回调

如下图所示: 在这里插入图片描述

# 参考

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

    我不想谋生,
    我想生活。
    红莲华
    x
    loading...