事件循环
浏览器的进程模型
何为进程?
程序运行需要专属的内存空间,可以把这块内存空间简单的理解为进程。
因此,官方话来说:进程是资源分配的最小单位(分配的就是内存空间)。
每个应用至少有一个进程,进程之间相互独立,即使要通信,也需要双方同意
何为线程?
有了进程后,就可以运行执行程序的代码。
允许代码的[人]可以称为线程。
因此,官方话来说:线程是操作系统可调度的最小单位,也是实际执行代码的单位
一个进程至少有一个线程。所以在进程开启后回自动创建一个线程来运行代码,该线程称为主线程。
如果程序需要同时执行多块代码,主线程会创建更多的线程来执行代码,所以一个进程中可以包含多个线程。
浏览器有哪些进程和线程?
浏览器是一个多进程多线程的应用程序
多线程可以理解,因为需要执行大量代码。
为何是多进程呢?
浏览器内部工作及其复杂,为了避免浏览器内部各个模块相互影响,为了减少连环崩溃的几率,当启动浏览器后,它会自动启动多个进程。
常见的浏览器的主要进程:
-
浏览器主进程。
主要负责界面显示,用户交互,子进程管理。浏览器主进程内部会启动多个线程处理不同的任务。
-
网络进程
负责加载网络资源
-
渲染进程
渲染进程启动后,会开启一个渲染主线程,负责执行HTML、CSS、JS代码。
默认情况下,浏览器会为每个标签页开启一个新的渲染进程,以避免不同的标签页之间相互影响。(进程之间相互隔离,互不干扰)
渲染主线程如何工作的?
渲染主线程是最繁忙的线程:
-
解析HTML
-
解析CSS
-
计算样式
-
布局
-
每秒把页面画60次
-
执行全局js代码
-
执行事件处理函数
-
执行计时器的回调函数
....
要处理这么多任务,主线程遇到了一个前所未有的难题:如何调度任务?
比如:
- 正在执行一个js函数,用户点击按钮,你该立即去执行按钮的回调函数吗
- 正在执行一个js函数,定时器到达了时间,应该立刻执行吗?
渲染主线程想出了一个绝妙的主意:排队
- 在最开始的时候,渲染主线程会进入一个无限循环
每一次循环会检查消息队列(事件队列)中是否有任务存在,如果有,就取出第一个任务执行,执行完一个后进入下一次循环;如果没有,则进入休眠状态。- 其他所有线程(包括其他进程的线程)可以随时向消息队列(任务队列)添加任务。新任务会加到消息队列的末尾。在添加新任务时,如果渲染主线程是休眠状态,就会唤醒并继续循环取任务,执行任务。
整个过程,就被称为事件循环
若干解释
何为异步?
代码执行过程中,会遇到无法立即处理执行的任务,如:
- 计时完成后需要执行的任务 ——
setTimeout、setInterVal - 网络通信 ——
XHR、Fetch - 事件 ——
addEventListener
如果让渲染主线程等待这些任务的时机到来,就会导致主线程长期处于 阻塞状态(主线程需要解析HTML、需要1s画60次页面等等,很忙!!!),从而导致浏览器卡死。
所以主线程无论如何都不能阻塞!!!
因此,浏览器使用异步的方式,渲染主线程永不阻塞。
比如计时器:①渲染主线程通知计时线程计时,当前任务结束。②渲染主线程获取下一个任务继续执行③计时任务结束后,计时线程将回调函数放入到事件队列末尾。
面试题:如何理解js的异步
面试题:如何理解js的异步
js是一门单线程语言,它运行在浏览器的js引擎线程中。
如果js代码只使用同步的方式,就极有可能导致js引擎线程产生阻塞,从而导致消息队列中的很多其他任务无法得到执行。这样一来,一方面会导致繁忙的js引擎线程白白的消耗时间,另一方面导致页面无法及时更新,造成卡死。
所以浏览器采用异步的方式来避免,当js引擎线程遇到了异步任务,如定时器、网络、事件监听,js引擎线程将任务交给其他线程去处理,
自身立即结束当前任务的执行,继续执行下一个任务,当其他线程完成时,再将回调函数加入到消息队列的末尾,等待js引擎线程调度执行。在这种异步模式下,最大限度的保证了单线程的流畅执行。
任务有优先级吗?
任务没有优先级,任务谁先来谁就执行,但是消息队列有优先级。
过去,使用的是宏任务和微任务两个概念。现在分为:
- 延时队列:用于存放计时器时间到达后的回调函数。优先级
中 - 交互队列:用于存放用户操作后产生的事件处理任务。优先级
高 - 微任务。promise.then的回调函数 、MutationObserver 。优先级
最高
注意:必须全局任务或者函数内局部任务执行完成后,才会对三个任务队列排序执行。
面试题:阐述一下js的事件循环
事件循环:是浏览器渲染线程的工作方式。它会不断地从消息队列(也叫任务队列)中取出第一个任务,添加到执行栈中执行。当执行栈为空时,就进入等待状态,等待其他线程将任务添加到消息队列中,并循环执行。
过去,消息队列只是简单的分为宏任务和微任务。宏任务比如事件处理,script标签导入,计时器。微任务MutationObserver,promise.then的回调函数等待。 现在w3c分的更加精细:分为延时队列、交互队列和微任务。微任务的优先级最高其次交互任务最后延时任务。
事件循环的总体流程:首先执行一个宏任务,如果有微任务将微任务推入到微任务队列中,然后执行微任务队列中所有的微任务。当所有微任务完成后,再执行下一个宏任务。