Js单线程、浏览器Event Loop、Microtasks 、Macrotasks

Js为什么设计为单线程

Javascript作为浏览器脚本语言,主要用途是与用户互动,以及操作 DOM。如果 JavaScript 是多线程的方式来操作这些 UI DOM,则可能出现 UI 操作的冲突。假设存在两个线程同时操作一个 DOM,一个负责修改一个负责删除,那么这个时候就需要浏览器来裁决如何生效哪个线程的执行结果。当然可以为浏览器引入"锁"的机制来解决这些冲突,但这会大大提高复杂性,所以 JavaScript 从诞生开始就选择设计为单线程。

为什么有事件循环

js是单线程,所以几乎所有代码都在一个线程上执行,在某一时刻内只能执行特定的一个任务,并且会阻塞其它任务执行。浏览器的每个渲染进程都有一个主线程,主线程 处理用户事件和页面绘制,它要运行一个页面中的所有 JavaScript 脚本,以及呈现布局,回流,和垃圾回收,要让这么多不同类型的任务在主线程中有条不紊地执行,就需要事件循环来统筹调度这些任务。

什么是事件循环

事件循环是一个无限循环,JavaScript 引擎等待任务,执行它们,然后休眠,等待更多任务, JavaScript 引擎大多数时候不执行任何操作,它仅在脚本、处理程序、事件激活时运行。

任务示例:

  • 当外部脚本<script src="...">加载时,任务就是执行它。
  • 当用户移动鼠标时,任务是调度mousemove事件并执行处理程序。
  • 当计划的时间到期时setTimeout,任务是运行其回调。
  • ...等等

如图,当引擎忙于执行任务时script,用户可能会移动鼠标产生任务mousemove,setTimeout等等,这些任务形成一个队列,队列中的任务按照"先到先服务"的原则进行处理。当引擎浏览器处理完 script后,它会处理mousemove事件,然后setTimeout处理处理程序。

除了宏任务,还有微任务,每个宏任务都有一个微任务队列,微任务完全来自用户的代码 ,比如 queueMicrotask(func),promise.then(func)。 在每个宏任务之后,引擎会立即执行微任务队列中的所有任务,然后再运行任何其他宏任务或渲染。 如果微任务中产生了新的微任务,新的微任务还是在当前的微任务队列中,所以如果在微任务中不停产生新的微任务,会阻塞页面!

宏任务:

  • script(整体代码)
  • setTimeout
  • setInterval
  • I/O 操作
  • UI交互事件
  • postMessage
  • MessageChannel
  • setImmediate(Node.js 环境)
  • ...

微任务:

  • Promise.then
  • queueMicrotask
  • Object.observe
  • MutaionObserver
  • process.nextTick(Node.js 环境)
  • ...

所有微任务都在任何其他事件处理或渲染或任何其他宏任务发生之前完成, 如果想在浏览器渲染更改或处理新的宏任务之前异步执行一个函数,就可以使用queueMicrotask、promise.then将该函数变成一个微任务。

用户看到的页面卡顿就是因为,如果某项任务花费的时间太长,浏览器就无法执行其他任务,当引擎执行其他任务时,渲染必须等待。

事件循环中有几个队列

根据 WHATWG规范,

An event loop has one or more task queues. A task queue is a set of tasks.

Per its source field, each task is defined as coming from a specific task source. For each event loop, every task source must be associated with a specific task queue.

一个 Event Loop 中,可以有一个或者多个任务队列(task queue),一个任务队列便是一系列有序任务(task)的集合,每个任务都有一个任务源(task source),源自同一个任务源的 task 必须放到同一个任务队列,从不同源来的则被添加到不同队列。比如鼠标事件的队列,IO完成消息队列,渲染任务队列,并且可以给这些任务队列排优先级。

但是浏览器的实现并没有完全按照规范来,具体浏览器实现了多少不同的任务队列,那得去看浏览器的源码,但是这些不同任务队列中的任务都是宏任务,可以看作一个宏任务队列和一个微任务队列

参考

推荐视频:

相关推荐
福兮说7 小时前
设计稿是 #4A7C6F,页面量出来是 #4B7C6F:HEX、HSL、透明度、canvas 来回转的七个坑
前端·javascript·css·canvas
郑州光合科技余经理7 小时前
海外版外卖加盟:总站与分站配送规则怎么分开管
java·开发语言·前端·后端·uni-app·php·ai编程
小呆呆6668 小时前
副业搞起来,小说,漫画,漫剧的成本优化思路
前端·后端·面试
凤城老人8 小时前
从 PyQt6 到 Electron:给 Edge TTS 做一个“多角色配音机“的踩坑手记
javascript·typescript·electron
Dovis(誓平步青云)8 小时前
浇水提醒刚弹出又消失,植物状态别只存一个百分比
开发语言·前端·javascript·pdf·ecmascript·电脑
码艺-Alimjan9 小时前
Vben Admin 新增维吾尔语 Vben-Modal的关键坑之一
前端·javascript·vue.js
可乐鸡翅yeah_9 小时前
hls.js 手动自定义 http 请求 loader,修改请求头实战
开发语言·前端·javascript·网络协议·http·ecmascript·m3u8在线
IT_陈寒9 小时前
Vite静态资源导入这个坑我帮你们踩过了
前端·人工智能·后端
广州华水科技10 小时前
大坝安全监测解决方案:单北斗GNSS形变监测系统应用与维护
前端
yivifu10 小时前
中文古籍电子书注释集成
前端·javascript·python·beautifulsoup·epub