浏览器线程与进程,JS执行机制

284 阅读4分钟

1.浏览器进程

image.png

  1. brower进程 :浏览器的主进程,只有一个
  2. 插件进程: 对应的插件,可以有多个
  3. GPU进程 : 最多一个,用于3D绘制等
  4. 渲染Render进程:又名‘浏览器内核’(重点)就是一个tab页签,包含所有的绘制,排版,渲染,事件等
  5. sharedworker进程 : 多个页面共享一个进程

2.渲染render进程下6个线程

image.png

  1. GUI渲染线程
  2. JS引擎线程
  3. 事件触发线程
  4. 定时触发线程
  5. 异步http请求线程
  6. wobworker线程

3.浏览器进程与线程的流程图

image.png

  1. 为了防止渲染冲突,gui线程render线程于js引擎线程 不能同时执行,互斥的关系。
  2. 当需要3d支持时候,启动GPU渲染 优化渲染效率(如上下滚动)

4.JS引擎的执行机制

背景

js是单线程的,代码都是同步执行,执行过程其他代码会阻碍,如大数据量I/O操作。要实现多线程操作,引入了事件循环机制Event Loop,实现代码异步操作,异步的代码交给其他线程处理(事件触发线程,定时触发线程,异步http请求线程),当处理结束后通过事件循环机制,一直从队列中回调执行。

概念:

  • 同步任务:标签的代码,都在主线程上执行,一解析就立即调用。也可以算一次宏任务。
  • 异步任务:分为 宏任务 和微任务。解析后,根据不同的方法加入不同的任务队列。
  • 宏任务 : 是一个队列,先进先出。
  • 微任务 : 是一个队列,先进先出。
  • 调用栈:代码先解析,然后推到执行栈,执行栈的代码都是同步执行。是后进先出。

核心逻辑

  1. 解析里面的代码,
  2. 遇到同步任务会加入执行栈,并立即执行。
  3. 遇到异步任务会根据不同的方法分别加入"宏任务"和“微任务”。
  4. 在所有代码执行完后,会优先清空当前所有"微任务"队列。
  5. 然后执行渲染。再进去下一次事件循环,由于

宏任务

事件循环每次只在宏队列读取一个宏任务执行。

script(整体代码)
setTimeout
setInterval
I/O
UI交互事件
postMessage
MessageChannel
setImmediate(Node.js 环境)

微任务

每次当前执行栈结束前,清空所有微队列里的任务。

微任务包含:

Promise.then
Object.observe
MutationObserver
process.nextTick(Node.js 环境)

执行流程

image.png 重点:每次执行完一个宏任务,都会清空所有微任务。

在同一次执行栈中,微任务会优于宏任务先执行。

  1. 先执行同步代码(注意第一次执行肯定没有任何异步的宏队列,所以只执行同步的代码,异步的宏任务忽略)
  2. 执行过程中遇到异步代码放入异步队列(可能有宏任务或微任务,宏只能等下次刷新后才执行一个,微任务可以在执行完同步后直接执行)
  3. 同步代码执行完后,会先执行刚刚入队的所有微任务。
  4. 页面进行重新渲染。
  5. 再执行同步代码,同时如果发现有异步的宏任务,会执行一个。
  6. 然后再执行2.的步骤一直循环 同步代码 -> 1个宏任务 -> 执行所有微任务 -> 渲染 -> 同步代码 -> 1个宏任务 -> 执行所有微任务 ->

测试代码

 Promise.resolve().then(function() {
            console.log("Promise A")
        }); 
        console.log("111");
        setTimeout(() => {
            console.log("模拟网络请求6秒");
        }, 6000);
        setTimeout(() => {
            console.log("setTimeout");
        }, 0); 
        console.log("222"); 
        Promise.resolve().then(function() {
            console.log("Promise B")
        }); 
        
//打印内容
//111
//222
//Promise A
//Promise B
//setTimeout     
//模拟网络请求6秒

image.png

注意:如果是then().then() //则依然在微任务中,不用等待下一次,直接入栈,等待前面所有已入栈的微队列结束后再出栈。 
 Promise.resolve().then(function() {
            console.log("Promise A")
        }).then(function() {
            console.log("Promise A1")
        }); 
        console.log("111");
        setTimeout(() => {
            console.log("模拟网络请求6秒");
        }, 6000);
        setTimeout(() => {
            console.log("setTimeout");
        }, 0); 
        console.log("222"); 
        Promise.resolve().then(function() {
            console.log("Promise B")
        }); 
        
//打印内容
//111
//222
//Promise A
//Promise B
//Promise A1
//setTimeout     
//模拟网络请求6秒