首页/文章/javascript

JS 微任务和宏任务

2022-11-23
339512 分钟
...

单线程和任务队列

  1. 单线程就意味着,所有任务需要排队,前一个任务结束,才会执行后一个任务。如果前一个任务耗时很长,后一个任务就不得不一直等待。
  2. 如果排队是因为计算量过大,CPU忙不过来,倒也算了,但是很多时候CPU是闲着的,因为IO设备(输入输出设备)很慢(比如Ajax操作从网络读取数据),不得不等着结果出来,再往下执行!
  3. JavaScript语言的设计者意识到,这时主线程完全可以不管IO设备,挂起处于等待中的任务,先运行排在后边的任务,等到IO设备返回了结果,再回过头把挂起的任务继续执行下去
  4. 于是,所有的任务可以分为两种,一种是同步任务(synchronous),另外一种是异步任务(asynchronous)。同步任务指的是,在主线程上,排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;异步任务指的是,不进入主线程,而进入“任务队列”(task queue)的任务,只有“任务队列”通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。

javascript事件循环

  1. 同步任务:直接通过主线程执行,如script代码
  2. 异步任务: 进入Event Table,并注册回调函数——> Event Queue,等主线程的执行栈为空时候,读取Event Queue里面的函数就,进入主线程。如setTimeout,promise.then()等;

==javascript是单线程的,但是浏览器是多线程的,典型的浏览器有如下线程:==

  • javascript引擎线程
  • 界面渲染线程
  • 浏览器事件触发线程
  • Http请求线程

关于javaScript的单线程

  • 应用场景决定了javascript的单线程的特性,假如javascript是多线程,同时进行:一个线程对某一个dom进行添加属性操作,另一个线程对该线程进行删除操作,那么浏览器该听哪一个。这就决定javascript必须是单线程。
  • web worker:是一个多线程。它出现的目的是当浏览器有大量密集的计算时候或者响应时间很长的运算时候,页面出现卡顿,可以起一个worker子线程,主线程和worker线程互不干预,这样页面就可以进行点击之类的操作,但这个子线程不能操作DOM元素。

任务队列

Js 中,有两类任务队列:宏任务队列(macro tasks)和微任务队列(microtasks)。宏任务队列可以有多个,微任务队列只有一个

  • 宏任务:script(全局任务), setTimeout, setInterval, setImmediate, I/O, UI rendering.
  • 微任务:process.nextTick (node.js中进程相关的对象), Promise, Object.observer, MutationObserver。

宏任务(macrotask )和微任务(microtask )

macrotask 和 microtask 表示异步任务的两种分类。

==宏任务(task)==:就是JS 内部(任务队列里)的任务,严格按照时间顺序压栈和执行。如 setTimeOut、setInverter、setImmediate 、 MessageChannel等

==微任务(Microtask )==:通常来说就是需要在当前 任务 执行结束后立即执行的任务,例如需要对一系列的任务做出回应,或者是需要异步的执行任务而又不需要分配一个新的 任务 ,这样便可以减小一点性能的开销。

在挂起任务时,JS 引擎会将所有任务按照类别分到这两个队列中,首先在 macrotask 的队列(这个队列也被叫做 task queue)中取出第一个任务,执行完毕后取出 microtask 队列中的所有任务顺序执行;之后再取 macrotask 任务,周而复始,直至两个队列的任务都取完。

宏任务与微任务的关系: 宏任务与微任务属性机制: 在这里插入图片描述 运行机制:

  1. 在执行栈中执行一个宏任务。

  2. 执行过程中遇到微任务,将微任务添加到微任务队列中。

  3. 当前宏任务执行完毕,立即执行微任务队列中的任务。

  4. 当前微任务队列中的任务执行完毕,检查渲染,GUI线程接管渲染。

  5. 渲染完毕后,js线程接管,开启下一次事件循环,执行下一次宏任务(事件队列中取)。

让我们先来看一段代码: 在这里插入图片描述

  1. 首先浏览器执行js进入第一个宏任务进入主线程, 直接打印console.log(‘1’)
  2. 遇到 setTimeout 分发到宏任务Event Queue中
  3. 遇到 process.nextTick 丢到微任务Event Queue中
  4. 遇到 Promise, new Promise 直接执行 输出 console.log(‘7’);
  5. 执行then 被分发到微任务Event Queue中``
  6. 第一轮宏任务执行结束,开始执行微任务 打印 6,8
  7. 第一轮微任务执行完毕,执行第二轮宏事件,执行setTimeout
  8. 先执行主线程宏任务,在执行微任务,打印’2,4,3,5’
  9. 在执行第二个setTimeout,同理打印 ‘9,11,10,12’
  10. 整段代码,共进行了三次事件循环,完整的输出为1,7,6,8,2,4,3,5,9,11,10,12。

以上是在浏览器环境下执行的数据,只作为宏任务和微任务的分析,我在node环境下测试打印出来的顺序为:1,7,6,8,2,4,9,11,3,10,5,12。node环境执行结果和浏览器执行结果不一致的原因是:浏览器的Event loop是在HTML5中定义的规范,而node中则由libuv库实现。libuv库流程大体分为6个阶段:timers,I/O callbacks,idle、prepare,poll,check,close callbacks,和浏览器的microtask,macrotask那一套有区别。

注意事项:

  1. 先是宏任务–>微任务–>宏任务–>微任务一直循环下去;
  2. script代码为第一层宏任务,如果有setTimeout,setInterval,则他们的回调函数会成为第二层的宏任务,
  3. promise.then()和process.nextTick()是微任务,在执行完该一层的宏任务后执行,==且process.nextTick()优先于promise.then();==

小结

  1. macrotask(按优先级顺序排列): script(你的全部JS代码,“同步代码”), setTimeout, setInterval, setImmediate, I/O,UI rendering
  2. microtask(按优先级顺序排列):process.nextTick,Promises(这里指浏览器原生实现的 Promise), Object.observe, MutationObserver
  3. JS引擎首先从macrotask queue中取出第一个任务,执行完毕后,将microtask queue中的所有任务取出,按顺序全部执行;
  4. 然后再从macrotask queue(宏任务队列)中取下一个,执行完毕后,再次将microtask queue(微任务队列)中的全部取出;
  5. 循环往复,直到两个queue中的任务都取完。

提别强调:

队列的优先级执行顺序为: ==先执行同步和立即执行任务>microtask>macrotask==

如果您觉得这篇文章有帮助,请点个赞吧~

分享文章

相关文章

更多文章 →
javascript2026-02-24
navigator.sendBeacon全指南
在前端开发中,埋点系统是必不可少的一环。我们经常需要在用户 关闭页面 、 刷新 或 跳转路由 时,向服务器发送最后一条统计数据(比如用户停留时长、页面跳出率)。 但这看似简单的需求,在实现时却危机四伏:请求发不出去?页面跳转卡顿?今天我们就来聊聊这个问题的终极解决方案 —— 。 一、 痛点与传统方案的挣扎 场景还原 当用户点击关闭按钮时,浏览器会触发生命周期事件( 或 )。如果我们直接使用普通的异步 AJAX ( 或 ) 发送请求,浏览...
学习
javascript2025-11-02
理解浏览器事件系统,从用户点击到事件对象的完整旅程
深入理解浏览器事件系统:从用户点击到事件对象的完整旅程 “当我点击页面按钮时,背后发生了什么?为什么回调函数能收到一个包含丰富信息的event对象?今天,让我们一起揭开浏览器事件系统的神秘面纱。” 一个令人困惑的现象 作为前端开发者,我们每天都在写这样的代码: 这段代码如此熟悉,以至于我们很少停下来思考:​ ​这个 对象到底从哪里来?它为什么能知道点击的精确坐标?为什么能识别是哪个元素被点击了?​ ​ 更神奇的是,当我们手动创建事件时:...
学习
javascript2025-10-01
实现大文件上传全流程详解
在日常开发中,大文件上传是个绕不开的坎——动辄几百 MB 甚至 GB 级的文件,直接上传不仅容易超时,还会让用户体验大打折扣。最近我用 Vue+Express 实现了一套完整的大文件上传方案,支持分片上传、断点续传、秒传和手动中。 一、先看效果:我们要实现什么? 先上核心功能清单,确保大家明确目标,知道我们要解决哪些实际问题: 大文件分片上传 :将文件切成固定大小的小片段分批上传,避免单次请求超时 秒传 :服务器已存在完整文件时,直接返...
学习
javascript2025-09-18
JavaScript 的多线程能力:Worker
如果你写过一些计算量稍大的 JavaScript 代码,比如图像处理、大量数据排序或者复杂的算法,你几乎肯定遇到过浏览器“卡死”的现象。点击页面没反应,动画也停了,就像整个世界都静止了。 这就是主线程被阻塞的典型后果。因为主线程既要负责执行 JavaScript,又要负责渲染页面、响应用户操作,一旦它被繁重的计算任务占满,就无暇顾及其他,用户体验便直线下降。 这个问题的根源,正是“主线程是单线程的”。那么,如何解决呢? 答案很简单:把这...
学习面试
javascript2025-09-15
一张 8K 海报差点把首屏拖垮
你给后台管理系统加了一个「企业风采」模块,运营同学一口气上传了 200 张 8K 宣传海报。首屏直接飙到 8.3 s,LCP 红得发紫。 老板一句「能不能像朋友圈那样滑到哪看到哪?」——于是你把懒加载重新翻出来折腾了一轮。 解决方案:三条技术路线,你全踩了一遍 1\. 最偷懒:原生 一行代码就能跑,浏览器帮你搞定。 🔍 关键决策点 2020 年后现代浏览器全覆盖,IE 全军覆没。 必须写死 ,否则 CLS 会抖成 PPT。 适用场景...
学习
javascript2025-09-10
🚀 Web Worker让你的应用丝滑
🌟 引言 在日常的前端开发中,你是否遇到过这样的困扰: 大数据处理时页面卡死 :处理几万条数据时,页面直接卡成PPT,用户点击毫无反应 复杂计算阻塞UI :图片处理、数据分析等计算密集型任务让整个应用假死 文件上传/下载卡顿 :大文件操作时,其他功能完全无法使用 实时数据处理性能差 :WebSocket接收大量数据时,页面渲染严重滞后 今天分享6个Web Worker的核心技巧,让你的应用告别卡顿,用户体验丝滑如德芙! 💡 核心技巧...
学习

评论

请登录后发表评论

去登录
加载评论中...

目录