js中的事件循环

浏览器进程模型

在理解什么叫事件循环前,我们需要先知道浏览器的进程模型
现代浏览器的功能极度复杂,为了能确保各个部分独立运行互不影响,浏览器会在启动之时开启多个进程,具体而言可以分为以下三种

  1. 浏览器进程
    负责浏览器的用户交互,子进程管理,浏览器页面显示等等,浏览器进程是最先被启动的进程,将由它来启动并维护其他进程
  2. 网络进程
    负责浏览器的网络通信,加载网络资源,会在内部开启多个线程来维护不同的网络任务
  3. 渲染进程
    渲染进程不同于其他进程,它一次只能开启一个线程,这个线程也被称为渲染主线程,将由它负责渲染页面,执行js等等

进程模型

默认情况下,现代浏览器都是开启一个页面则开启一个渲染进程,具体可以通过浏览器的任务管理器来查看

管理器

渲染主线程

渲染主线程可以说是浏览器中最繁忙的线程,因为各种各样的原因导致渲染进程一次只能开启一个渲染主进程,所以主线程将负责包括但不限于以下内容

  1. 解析HTML
  2. 解析CSS
  3. 执行JS代码
  4. 处理图层

可以看到渲染主线程的任务多且杂,但渲染主线程只有一个,一次只能执行一个任务,那任务与任务之间又如何调度

渲染主线程的解决方案是将任务排队

消息队列

渲染主线程会维护消息队列,所有任务都需要按照先来后到的顺序放入消息队列中,渲染主线程则会依次取出 消息队列中的任务执行,具体有以下步骤

  1. 最开始渲染主线程会开启一个死循环
  2. 每一次循环渲染主线程都会检测消息队列中是否有任务,如果有就取出执行,没有就进入休眠状态
  3. 其他线程可以随时向消息队列中添加任务,如果此时渲染主线程处于休眠状态则会被唤醒

基于这样一个循环,我们的渲染主线程就能有条不紊的执行下去了

在这里插入图片描述

异步

到现在为止一切似乎都没什么问题,任务在消息队列的调度下能有序执行,最大的困难便解决了,但任务不全都是同步执行,有些任务浏览器遇到了之后并不立刻执行,如setTimeOutsetIntervalPromise等等,如果直接将其当做同步任务执行的话就会导致页面阻塞

阻塞
为了保证用户的使用体验,使页面不被卡死,因此浏览器采用异步的方式来解决这个问题,具体步骤如下

  1. 当渲染主线程遇到异步任务时会通知对应的线程,当前任务结束,从消息队列取出任务继续执行
  2. 其他线程开始监控,待任务到达触发时机就会将任务的回调函数包装成任务放入消息队列中
  3. 渲染主线程会自始至终从消息队列中取出任务执行

异步任务处理
当然,所有异步任务不可能都由一个线程处理,不同的异步任务会有对应的线程接管

浏览器通过使用异步的形式来使渲染主线程不会出现阻塞

由此我们能得到两个结论

单线程是异步产生的原因
事件循环是异步的实现方式

任务优先级

到了这里,似乎事件循环我们已经了解的差不多了,但还有一些疑问我们仍未解决,任务与任务之间有优先级的区别吗
事实上,任务与任务之间并没有优先级的区别,都是先进先出,但消息队列与消息队列间有着不同的优先级
消息队列向来都不只有一个,具体可以分为宏队列微队列
然而随着现代浏览器复杂度的急剧提升,原有的划分方式已不满足于现在需要,所以目前的消息队列划分如下

  1. 每个任务都会有一个任务类型同一个类型的任务必须在同一个队列不同类型的任务可以在同一个队列
  2. 每个浏览器都必须拥有一个微队列微队列里的任务优先于其他队列里的任务执行
  3. 其他队列里的任务浏览器可以按照实际情况执行

在这里插入图片描述

事件循环

最后,我们再来总结一下什么是事件循环

事件循环是浏览器渲染主线程的主要工作方式,因为各种各样的原因,浏览器开辟的渲染进程只会存在一个渲染主线程,这也就决定js是一门单线程的语言,同时渲染主线程负责的任务十分繁杂,包括渲染HTML,CSS,执行JS,每秒重绘多少多少次,布局等等,为了保证所有任务都能稳定有序执行,浏览器会维护消息队列来存放任务,其他线程可以向消息队列中提交任务,渲染主线程每次都会从消息队列中取出第一个任务来执行,如果没有任务渲染主线程就会进入休眠,待消息队列中有了新的任务就会被唤醒,如果是异步任务则会交由其他线程托管,待任务需要执行时将事件的回调函数包装成任务放入消息队列中。根据W3C规定,每个任务都有一个消息类型,同一类型的任务必须要在同一个消息队列中,不同类型的任务可以在一个消息队列中,每个浏览器必须要有一个微队列,并且微队列中的任务要先于其他消息队列中的任务执行。

关于JS中能否做到精准计时

严格上来说是不能的,主要原因有以下几点

  1. 计算机内没有原子钟,无法做到精确计时
  2. js的计时也是调用系统级的函数,会有些许偏差
  3. W3C规定计时器嵌套超过4层的话从第5层开始强制计时器有5ms延时
  4. 当计时器结束,任务进入消息队列也不是立刻执行的,需要等待渲染主线程的调用,这也会有时间上的偏差
  • 12
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值