概述

事件轮询是计算机系统的一种运行机制,JS采用这种机制,来解决单线程与异步的一些问题。

单线程的问题

  • 所有任务都在一个线程下执行,遇到大量任务或耗时任务,就会导致网页假死,无法进行交互
  • 并且如果一个任务阻塞了,后续的代码都会被一直搁置

因此就产生了异步的概念,即通过其他线程去完成某些耗时操作,如AJAX、定时器等

  • 运行环境会为异步操作提供一个单独的线程,因为不在主线程中执行,所以不会造成阻塞
  • 但是异步也导致结果不可预测,无法同步获得结果,只能通过 事件 + 异步回调 的方式
  • 但当事件触发时,无法保证 JS 线程是空闲的,异步回调无法立刻执行

事件轮询机制,解决了上述的问题

  • 事件轮询中有个 任务队列,异步任务触发事件时,会将对应的回调添加到这个队列中
    • 这就缓存了回调,且队列是有序的,保证了回调正常有序的执行
  • 当JS线程空闲时,就会查询任务队列,并逐个执行其中的任务

整体流程

640.gif

  • 运行环境会为异步操作提供单独的线程
  • 当异步任务执行完毕后,会触发指定的事件,并将对应事件的回调添加到任务队列中
  • JS引擎执行完同步任务,栈空闲时,就会将队列中的回调压入栈中同步执行,以此循环
  • 当队列为空时,JS引擎就会等待,直到有新的回调进入队列,然后开启下一个循环

任务队列(task queue)

599584-15f617d44cdb990d.webp

  • 专门用来存放待执行任务的队列,任务:异步任务触发事件时,对应需要执行的异步回调
  • 一个JS线程只会拥有一个事件循环,但是可以有多个任务队列,根据任务类型分为两个
    • 宏队列(macro-task queue):存放宏任务的队列
    • 微队列(macro-task queue):存放微任务的队列
  • 轮询顺序:在不同的环境中,两条队列有着不同的轮询顺序
    • 浏览器:执行完一个宏任务后,立刻轮询微队列,微任务全部执行完成后,再从宏队列开始轮询
    • Node:先轮询宏队列,直到宏队列全部执行完成后,再去轮询微队列

宏任务、微任务

不同队列的任务分为两种

  • 宏任务(macro-task):script内的代码,定时器回调,I/O,UI rending
  • 微任务(micro-task):promise回调,MutationObserver回调,process.nextTick回调
  • 在最新标准中,分别称为 task 和 jobs

如Promise/定时器称之为任务源,它们的事件回调才是任务
**