消息队列和事件循环

每个渲染进程都有一个主线程,并且主线程非常繁忙,既要处理DOM,又要计算样式,还要处理布局,同时还需要处理JavaScript任务以及各种输入事件。为了让这些不同类型的任务在主线程中有条不紊的执行,就需要一个系统来统筹调度这些任务,这个统筹调度系统就是消息队列和事件循环系统

第一版:使用单线程处理安排好的工作

​ 线程的一次执行

第二版:在线程中引入事件循环

原因:并不是所有的任务都是在执行之前统一安排好的,大部分情况下,新的任务是在线程运行过程中产生的。

解决:要想在线程运行过程中,能接收并执行新的任务,就需要采用事件循环机制。即:

  • 引入了循环机制,具体实现方法是在线程语句最后添加了一个for循环语句,线程会一直循环执行。
  • 引入了事件,可以在线程运行过程中,等待用户输入的数字,等待过程中线程处于暂停状态,一旦接收到用户输入的信息,那么线程会被激活,然后执行相加运算,最后输出结果。

​ 在线程中引入事件循环

第三版:队列 + 循环

原因:在第二版的线程模型中,所有的任务都是来自于线程内部的,如果另外一个线程想让主线程执行一个任务,第二版的线程模型无法做到。

解决:使用消息队列

消息队列是一种数据结构,可以存放要执行的任务。符合队列“先进先出”的特点

从上图可以看出,改造分三步:

  • 添加一个消息队列
  • IO线程中产生的新任务添加进消息队列尾部
  • 渲染主线程会循环地从消息队列头部中读取任务,执行任务。

跨进程发送消息

从图中可以看出,如果其他进程想要发送任务给页面主线程,那么先通过IPC把任务发送给渲染进程的IO线程,IO线程再把任务发送给页面主线程。

页面主线程如何安全退出

在Chrome中,当页面主线程执行完成后,确定要退出当前页面时,页面主线程会设置一个退出标志的变量,在每次执行完后判断是否有退出标志,若有,则直接中断当前的所有任务。

页面使用单线程的缺点

  • 如何处理高优先级的任务

若采用观察者模式,设计监听接口,则影响执行效率。若采用异步,则影响实时性。

解决方案:微任务

通常我们把消息队列中的任务称为宏任务,每个宏任务中都包含了一个微任务队列,在执行宏任务的过程中,如果 DOM 有变化,那么就会将该变化添加到微任务列表中,这样就不会影响到宏任务的继续执行,因此也就解决了执行效率的问题。

等宏任务中的主要功能都直接完成之后,这时候,渲染引擎并不着急去执行下一个宏任务,而是执行当前宏任务中的微任务,因为 DOM 变化的事件都保存在这些微任务队列中,这样也就解决了实时性问题。

  • 如何解决单个任务执行时长过久,导致卡顿的问题

解决方案:JavaScript通过回调功能来规避这种问题,也就是让要执行的JavaScript任务滞后执行。

微任务

  • 为什么需要微任务?

因为 JavaScript 代码不能掌控宏任务添加到队列中的位置,难以控制开始执行任务的时间。对时间精度要求较高的需求,宏任务难以胜任,所以需要微任务。

  • 什么是微任务?

微任务是一个需要异步执行的函数,执行时机是在主函数执行结束之后、当前宏任务结束之前

  • 如何产生微任务?

    • 使用 MutationObserver 监控某个 DOM 节点,然后再通过 JavaScript 来修改这个节点,或者为这个节点添加、删除部分子节点,当 DOM 节点发生变化时,就会产生 DOM 变化记录的微任务。

    • 使用 Promise,当调用Promise.resolve()或者Promise.reject()的时候,也会产生微任务。

  • 微任务和宏任务

    • 微任务和宏任务是绑定的,每个宏任务在执行时,会创建自己的微任务队列。

    • 微任务的执行时长会影响到当前宏任务的时长。

    • 在一个宏任务中分别创建一个用于回调的宏任务和微任务,无论什么情况下,微任务都早于宏任务执行。

宏任务是开会分配的工作内容,微任务是工作过程中被临时安排的内容

原文地址:https://www.cnblogs.com/zpsakura/p/13885400.html