· 859 words · 5 min
每个渲染进程都有一个主线程,并且主线程非常繁忙,要处理 DOM、样式,布局、JS 任务以及各种输入事件。 要让这么多不同类型的任务在主线程中有条不紊地执行,这就需要一个系统来统筹调度这些任务, 这个统筹调度系统就是我们今天要讲的消息队列和事件循环系统。
并不是所有的任务都是在执行之前统一安排好的,大部分情况下,新的任务是在线程运行过程中产生的。 要想在线程运行过程中,能接收并执行新的任务,就需要采用事件循环机制。事件循环机制的特点一是引入了循环机制, 二是引入了事件(例如等待用户输入,此时线程处于暂停状态)。
只有事件循环机制是无法处理其他线程发来的任务的,从上图可以看出,渲染主线程会频繁接收到来自于 IO 线程的一些任务, 接收到这些任务之后,渲染进程就需要着手处理。使用消息队列机制可以让其接受其他线程发送的消息。
消息队列是一种数据结构,可以存放要执行的任务。它符合队列先进先出的特点,也就是说要添加任务的话,添加到队列的尾部; 要取出任务的话,从队列头部去取。
渲染进程有一个 IO 线程专门用来接受其他进程传来的消息。接收到消息之后,拼装成任务发送给渲染主线程。
线程按顺序处理 DOM 变化的任务时,当任务执行时间太长时,会导致线程的执行效率下降。如果采取异步的方式, 又会影响到监控的实时性。针对这种情况,微任务就应用而生了。
通常我们把消息队列中的任务称为宏任务,每个宏任务中都包含了一个微任务队列,在执行宏任务的过程中, 如果 DOM 有变化,那么就会将该变化添加到微任务列表中,这样就不会影响到宏任务的继续执行,因此也就解决了执行效率的问题。
等宏任务中的主要功能都直接完成之后,这时候,渲染引擎并不着急去执行下一个宏任务,而是执行当前宏任务中的微任务, 因为 DOM 变化的事件都保存在这些微任务队列中,这样也就解决了实时性问题。
因为所有的任务都在单线程中执行,每次只能执行单个任务,其他任务就处于等待状态。 当一个任务执行时间过久,下个任务就要等待很久的时间。
针对这种情况,JS 可以通过回调功能来规避这种问题,也就是让要执行的 JavaScript 任务滞后执行。