Open hawx1993 opened 1 year ago
首先,我们来思考个问题:Chrome 浏览器是多进程还是单进程,是多线程还是单线程?
我们可以打开mac的活动监视器,如下图:
从上图我们可以看出Chrome浏览器是一个多进程且多线程的复杂应用,那么什么是线程,什么是进程呢?
浏览器从关闭到启动,然后新开一个页面至少需要:1个浏览器主进程,1个GPU进程,1个网络进程,多个渲染进程,和多个插件进程。Chrome会尽可能为每一个tab甚至是页面里面的每一个iframe都分配一个单独的进程。
1个浏览器主进程
1个GPU进程
1个网络进程
多个渲染进程
多个插件进程
所以,单个网页tab是进程,内部分了若干个线程。
那么,参与到网页渲染生成流程中,有哪些线程?
大概有如下几个主要的线程:
setTimeout
setInterval
JS分为同步任务和异步任务,同步任务都在主线程上执行,形成一个执行栈(JS引擎线程)。 主线程之外,事件触发线程维护任务队列,异步任务有了回调,就在队列中插入一个事件 一旦执行栈中所有同步执行完成,系统就会读取任务队列,并且把可运行的异步任务转移到可执行栈中。
🤔思考一下,Chrome浏览器为什么要使用多进程架构呢?
举个例子,假如你有三个tab,你就会有三个独立的渲染进程。当其中一个tab的崩溃时,你可以随时关闭这个tab并且其他tab不受到影响。可是如果所有的tab都跑在同一个进程的话,它们就会有连带关系,一个挂全部挂。
渲染进程负责标签(tab)内发生的所有事情。在渲染进程里面,主线程(main thread)处理了绝大多数你发送给用户的代码。如果你使用了web worker或者service worker,相关的代码将会由工作线程(worker thread)处理。合成(compositor)以及光栅(raster)线程运行在渲染进程里面用来高效流畅地渲染出页面内容。
渲染进程的主要任务是将HTML,CSS,以及JavaScript转变为我们可以进程交互的网页内容。
为了防止渲染出现不可预期的结果,浏览器设置 GUI 渲染线程与 JavaScript 引擎为互斥的关系。当 JavaScript 引擎执行时, GUI 线程会被挂起,GUI 更新会被保存在一个队列中等到引擎线程空闲时立即被执行。
由上文可知,浏览器不是单线程的,浏览器的内核是多线程的,但为什么JavaScript是单线程的呢?
单线程的意思就是同一个时间内只能做一件事,JavaScript主要用户是于用户互动以及操作DOM。 这决定了他只能是单线程,否则会带来很多问题。比如:
假定JavaScript同时有两个线程,一个线程在某个DOM节点上添加内容,另一个线程删除了这个节点,这时浏览器应该以哪个线程为准?
js是单线程的原因不是js语言的特性,是浏览器只给js分配了一个线程的原因;
单线程的缺点是什么
Web Workers始终只能由一个主线程去更新页面,因为Web Workers内代码不能操作DOM更新UI(workers内无window)
单线程就意味着,所有任务需要排队,前一个任务结束,才会执行后一个任务。js引擎执行异步代码而不用等待,是因有为有 消息队列和事件循环。
消息队列:消息队列是一个先进先出的队列,它里面存放着各种消息。 事件循环:事件循环是指主线程重复从消息队列中取消息、执行的过程。
答案是js引擎在不停地检查,一遍又一遍,只要同步任务执行完了,引擎就会去检查那些挂起来的异步任务(异步任务(各种浏览器事件、定时器等)都是先添加到“任务队列”中。),是不是可以进入主线程了。这种循环检查的机制,就叫做事件循环。
除了广义的同步任务和异步任务,JavaScript 单线程中的任务可以细分为宏任务和微任务。
每个浏览器环境,至多有一个event loop。一个event loop可以有1个或多个task queue(任务队列),任务又分为Micro Task(微任务,比如:process.nextTick,Promise)和Macro Task(宏任务,比如:I/O,UI rendering,setTimeout和setInterval,requestAnimationFrame)——按执行先后顺序排序。
第一次事件循环中,JavaScript 引擎会把整个 script 代码当成一个宏任务执行,执行完成之后,再检测本次循环中是否存在微任务,存在的话就依次从微任务的任务队列中读取执行完所有的微任务,再读取宏任务的任务队列中的任务执行,再执行所有的微任务,如此循环。JS 的执行顺序就是每次事件循环中的宏任务-微任务。
每次微任务执行之后宏任务执行之前,如果有UI操作页面会重新渲染,一般页面刷新率是60HZ/秒,一帧是16.6毫秒,所以可以理解为事件循环每次轮询的时间大概是16.6毫秒
React 16实现了新的调度策略(Fiber), 新的调度策略提到的异步、可中断,其实就是基于浏览器的 requestIdleCallback和requestAnimationFrame两个API。
setTimeout函数是放在定时触发器线程的,当计时结束,开始执行回调函数的时候,是把代码放到js队列里面,而js是按照队列执行的,所以即使是setTimeout函数哪怕设置执行的时间为0,也不会立刻执行,因为要按照队列执行,这个不是异步的原因。
什么是进程与线程?
首先,我们来思考个问题:Chrome 浏览器是多进程还是单进程,是多线程还是单线程?
我们可以打开mac的活动监视器,如下图:
从上图我们可以看出Chrome浏览器是一个多进程且多线程的复杂应用,那么什么是线程,什么是进程呢?
浏览器进程
单个网页tab是进程还是线程?
浏览器从关闭到启动,然后新开一个页面至少需要:
1个浏览器主进程
,1个GPU进程
,1个网络进程
,多个渲染进程
,和多个插件进程
。Chrome会尽可能为每一个tab甚至是页面里面的每一个iframe都分配一个单独的进程。所以,单个网页tab是进程,内部分了若干个线程。
那么,参与到网页渲染生成流程中,有哪些线程?
大概有如下几个主要的线程:
setTimeout
和setInterval
所在的线程。浏览器定时计数器并不是由 JS 引擎计数的,因为 JS 是单线程的,如果处于阻塞线程状态就会影响计时的准确,所以通过单独的线程来计时并触发定时更为合理。JS分为同步任务和异步任务,同步任务都在主线程上执行,形成一个执行栈(JS引擎线程)。 主线程之外,事件触发线程维护任务队列,异步任务有了回调,就在队列中插入一个事件 一旦执行栈中所有同步执行完成,系统就会读取任务队列,并且把可运行的异步任务转移到可执行栈中。
多进程的好处
🤔思考一下,Chrome浏览器为什么要使用多进程架构呢?
举个例子,假如你有三个tab,你就会有三个独立的渲染进程。当其中一个tab的崩溃时,你可以随时关闭这个tab并且其他tab不受到影响。可是如果所有的tab都跑在同一个进程的话,它们就会有连带关系,一个挂全部挂。
渲染器进程
渲染进程负责标签(tab)内发生的所有事情。在渲染进程里面,主线程(main thread)处理了绝大多数你发送给用户的代码。如果你使用了web worker或者service worker,相关的代码将会由工作线程(worker thread)处理。合成(compositor)以及光栅(raster)线程运行在渲染进程里面用来高效流畅地渲染出页面内容。
渲染进程的主要任务是将HTML,CSS,以及JavaScript转变为我们可以进程交互的网页内容。
为了防止渲染出现不可预期的结果,浏览器设置 GUI 渲染线程与 JavaScript 引擎为互斥的关系。当 JavaScript 引擎执行时, GUI 线程会被挂起,GUI 更新会被保存在一个队列中等到引擎线程空闲时立即被执行。
为什么JavaScript是单线程?
由上文可知,浏览器不是单线程的,浏览器的内核是多线程的,但为什么JavaScript是单线程的呢?
单线程的意思就是同一个时间内只能做一件事,JavaScript主要用户是于用户互动以及操作DOM。 这决定了他只能是单线程,否则会带来很多问题。比如:
假定JavaScript同时有两个线程,一个线程在某个DOM节点上添加内容,另一个线程删除了这个节点,这时浏览器应该以哪个线程为准?
js是单线程的原因不是js语言的特性,是浏览器只给js分配了一个线程的原因;
Web Workers始终只能由一个主线程去更新页面,因为Web Workers内代码不能操作DOM更新UI(workers内无window)
Q1:JavaScript是单线程,怎样执行异步的代码?
单线程就意味着,所有任务需要排队,前一个任务结束,才会执行后一个任务。js引擎执行异步代码而不用等待,是因有为有 消息队列和事件循环。
消息队列:消息队列是一个先进先出的队列,它里面存放着各种消息。
事件循环:事件循环是指主线程重复从消息队列中取消息、执行的过程。
Q2:JavaScript 引擎怎么知道异步任务有没有结果,能不能进入主线程呢?
答案是js引擎在不停地检查,一遍又一遍,只要同步任务执行完了,引擎就会去检查那些挂起来的异步任务(异步任务(各种浏览器事件、定时器等)都是先添加到“任务队列”中。),是不是可以进入主线程了。这种循环检查的机制,就叫做事件循环。
除了广义的同步任务和异步任务,JavaScript 单线程中的任务可以细分为宏任务和微任务。
每个浏览器环境,至多有一个event loop。一个event loop可以有1个或多个task queue(任务队列),任务又分为Micro Task(微任务,比如:process.nextTick,Promise)和Macro Task(宏任务,比如:I/O,UI rendering,setTimeout和setInterval,requestAnimationFrame)——按执行先后顺序排序。
第一次事件循环中,JavaScript 引擎会把整个 script 代码当成一个宏任务执行,执行完成之后,再检测本次循环中是否存在微任务,存在的话就依次从微任务的任务队列中读取执行完所有的微任务,再读取宏任务的任务队列中的任务执行,再执行所有的微任务,如此循环。JS 的执行顺序就是每次事件循环中的宏任务-微任务。
每次微任务执行之后宏任务执行之前,如果有UI操作页面会重新渲染,一般页面刷新率是60HZ/秒,一帧是16.6毫秒,所以可以理解为事件循环每次轮询的时间大概是16.6毫秒
React 16实现了新的调度策略(Fiber), 新的调度策略提到的异步、可中断,其实就是基于浏览器的 requestIdleCallback和requestAnimationFrame两个API。
Q3:setTimeout函数哪怕设置执行的时间为0,看起来依然像延迟执行一样,是因为异步?还是放到了js其他线程执行了?
setTimeout
函数是放在定时触发器线程的,当计时结束,开始执行回调函数的时候,是把代码放到js队列里面,而js是按照队列执行的,所以即使是setTimeout
函数哪怕设置执行的时间为0,也不会立刻执行,因为要按照队列执行,这个不是异步的原因。