浏览器内核及event loop相关

一、浏览器有哪些进程

  • 浏览器主进程:主要负责显示界面、提供前进后退收藏等交互行为,提供子进程管理功能、存储用户数据。
  • GUP进程:GPU 的使用初衷是为了实现 3D CSS 的效果,只是随后网页、Chrome 的 UI 界面都选择采用 GPU 来绘制,这使得 GPU 成为浏览器普遍的需求。最后,Chrome 在其多进程架构上也引入了 GPU 进程
  • 网络进程:主要负责页面的网络资源加载。
  • 插件进程:主要是负责插件的运行,因插件易崩溃,所以需要通过插件进程来隔离,以保证插件进程崩溃不会对浏览器和页面造成影响。
  • 渲染进程:又称之为浏览器内核。

二、浏览器内核有哪些线程

  • GUI线程
负责把html解析成dom tree,把css解析成css rule,然后把两者结合,形成render tree。然后计算出layout tree放入浏览器内存中供浏览器主进程显示到界面上;
当界面需要重绘或回流时,该线程会被执行;
该线程与JS引擎线程是互斥的,当JS引擎在执行时,该线程会处于暂停状态。这也就是为什么一段JS代码执行时间比较久时页面会出现空白或卡顿的原因
 
  • JS引擎线程(单线程执行)
又叫JS内核,负责解析和执行JS代码.为什么JS是单线程执行的?假如JS是多线程执行的,一个线程要添加DOM、另一个线程要删除DOM,就会乱套,这就像两台电脑不能同时使用同一台打印机一样的道理;
 
JS是单线程执行的、那Worker为何可以达到多线程的效果?
创建WebWorker时,JS引擎线程向浏览器申请了worker专用的线程做为js引擎线程的子线程,两者通过postMessage Api进行线程间的通讯。
ShardWorker是浏览器中所有tab标签共享的,每个tab标签都有独立的render进程,所以SharedWorker不属于某个Render进程,而是浏览器开了独立的进程来管理。
 
  • 事件触发线程
主要负责事件队列的维护,当事件触发时会被事件线程捕获到,事件线程把事件处理程序放入到事件队列中,这一过程不会打断JS引擎线程的执行
 
  • 定时器触发线程
setTimeout与setInterval所在的线程;
由于js引擎是单线程的,如果处于阻塞线程状态,则会影响记时的准确性,因此需要单独的线程来计时并触发事件
当到达指定时间时,setTimeout回调代码也会被加入到事件队列,注意是加入事件队列,而不是立即执行,因为如果在队列中还有其他待执行的代码时就不会执行;
setInterval的情况更特殊,当到达指定时间时,如果上一次的回调函数还在队列中等待执行,则直接跳过而不做任何操作。
 
  • 异步请求线程
XmlHttpRequest所在线程;
假如不采用单独的线程而是直接使用JS引擎线程,从发出请求到等待服务端给出响应的这段时间,JS引擎就会一直处于运行状态,原本可以继续执行后面的代码的,现在只能等待
 

三、event-loop

js执行是单线程的,为了能让UI渲染、脚本执行、各种事件、网络请求等进行协同工作,将任务分成了同步任务和异步任务来避免阻塞问题,同步任务直接压入调用栈中等待js引擎所在线程执行,异步任务会在有了结果后将注册的回调函数放入到相应的任务队列,等待调用栈为空时去采用轮询机制去执行。

异步任务分为宏任务队列(Macro Task Queue)和微任务队列(Micro Task Queue),不同的异步任务会被放入不同的队列中。

  • 宏任务:包括setTimeoutsetInterval、I/O操作。
  • 微任务:包括Promise的回调、process.nextTick(Node.js)、MutationObserver

执行栈(call stack)中的同步任务会被按顺序执行,直到栈为空,然后开始执行异步任务:

  1. 执行所有微任务,直到微任务队列为空。
  2. 执行一个宏任务
  3. 重复以上步骤。

 

经典面试题:
async function async1(){
    console.log("async1 start")
    await async2()
    console.log("async1 end")
}
async function async2(){
    console.log("async2")
}  
console.log("script start")

setTimeout(function(){
    console.log("settimeout")
},0)
new Promise(function(resolve){
    console.log("promise")
    resolve()
}).then(function(){
    console.log("promise then")
})
async1()

 

执行顺序

  1. 同步代码(栈中的任务)

    • console.log("script start")"script start"
    • setTimeout 被添加到宏任务队列。
    • new Promise 的同步代码 console.log("promise")"promise"
    • .then() 的回调 console.log("promise then") 被添加到微任务队列。
  2. 执行 async1()

    • console.log("async1 start")"async1 start"
    • 调用 async2()console.log("async2")"async2"
    • 执行到 await async2() 时,async1 会暂停,等待 async2() 执行完成。
  3. 执行微任务队列

    • 在执行栈清空后,微任务队列中的任务会被立即执行。
    • 微任务队列中的任务顺序:Promise.then() 回调会首先被执行,console.log("promise then")"promise then"
  4. 继续执行 async1()

    • 执行 console.log("async1 end")"async1 end"
  5. 执行宏任务队列

    • 在微任务队列中的所有任务执行完成后,事件循环会去执行宏任务队列中的任务。
    • setTimeout 的回调会被执行,console.log("settimeout")"settimeout"
 
 
posted @ 2025-02-17 15:16  我是格鲁特  阅读(17)  评论(0)    收藏  举报