JS的执行原理,一文了解Event Loop事件循环、微任务、宏任务

大家好,我是宝哥

本文将带你深入探讨JavaScript这门语言的运行原理,揭开事件循环、微任务和宏任务隐藏在幕后的奥秘。


面试官:你了解JavaScript事件循环吗,掌握多少,把你知道的都说一下。

今天我们就来说一下,JavaScript作为一门单线程语言,如何通过事件循环(Event Loop)和任务队列(Task Queue)的机制,高效地处理异步任务,保证用户体验的流畅性。在本文中,我们将详细探讨事件循环、任务队列,以及在实际开发中的一些应用场景(面试常见笔试题)。

事件循环概述

在事件循环中,当主线程执行完当前的同步任务后,会检查事件队列中是否有待处理的事件。如果有,主线程会取出事件并执行对应的回调函数。这个循环的过程被称为事件循环(Event Loop),它由主线程任务队列两部分组成。主线程负责执行 同步任务,而 异步任务则通过任务队列进行处理。这种机制保证了异步任务在适当的时机能够插入执行,从而实现了JavaScript的非阻塞异步执行。

47a7565e6632d874a638a269b64b0545.jpeg

事件循环流程如下:

  1. 主线程读取JavaScript代码,形成相应的堆和执行栈。

  2. 当主线程遇到异步任务时,将其委托给对应的异步进程(如Web API)处理。

  3. 异步任务完成后,将相应的回调函数推入任务队列。

  4. 主线程执行完同步任务后,检查任务队列,如果有任务,则按照 先进先出的原则将任务推入主线程执行。

  5. 重复执行以上步骤,形成事件循环。

同步任务

同步任务是按照代码的书写顺序一步一步执行的任务。当主线程执行同步任务时,会阻塞后续的代码执行,直到当前任务执行完成。典型的同步任务包括函数调用、变量赋值、算术运算等。例如:

console.log('Step 1');
let result = add(2, 3);
console.log(result);
console.log('Step 2');
function add(a, b) {
  return a + b;
}

在上面的例子中, console.log('Step 1') 执行完毕后才会执行函数调用 add(2,3),并等待 add 函数返回结果后才会继续执行后续代码。

异步任务

异步任务是在主线程执行的同时,通过回调函数或其他机制委托给其他线程或事件来处理的任务。在执行异步任务时,主线程不会等待任务完成,而是继续执行后续代码。包括:

  • 回调函数 callback

  • Promise/async await

  • Generator

  • 事件监听

  • 发布/订阅

  • 计时器

  • requestAnimationFrame

  • MutationObserver

  • process.nextTick

  • I/O操作

不得不说,异步执行的机制使得 JavaScript 能够更好地处理耗时操作,保持页面的响应性。

console.log('Start');
setTimeout(() => {
  console.log('Timeout callback');
}, 1000);
console.log('End');

在上述例子中, setTimeout 是一个异步任务,它会在1秒后将回调函数推入任务队列,而主线程不会等待这个1秒,而是继续执行后面的 console.log('End')。当主线程的同步任务执行完成后,它会检查任务队列,将异步任务的回调函数推入执行栈,最终输出 'Timeout callback'

任务队列

上面我们讨论了同步任务和异步任务的执行过程,接下来我们将进一步探讨任务队列,了解它的最小颗粒度是如何执行的。

任务队列类型

任务队列分为 宏任务队列(macrotask queue)和 微任务队列(microtask queue)两种。JavaScript 引擎遵循事件循环的机制,在执行完当前宏任务后,会检查微任务队列,执行其中的微任务,然后再取下一个宏任务执行。这个过程不断循环,形成事件循环。

1、宏任务(Macrotasks)是一些较大粒度的任务,包括:

  • 所有同步任务

  • I/O操作,如文件读写、数据库数据读写等

  • setTimeout、 setInterval

  • setImmediate(Node.js环境)

  • requestAnimationFrame

  • 事件监听回调函数等

  • ...

2、微任务(Microtasks)是一些较小粒度、高优先级的任务,包括:

  • Promise的 then、 catch、 finally

  • async/await中的代码

  • Generator函数

  • MutationObserver

  • process.nextTick(Node.js 环境)

  • ...

任务执行过程

首先,必须要明确,在JavaScript中,所有任务都在主线程上执行。任务执行过程分为同步任务和异步任务两个阶段。异步任务的处理经历两个主要阶段:EventTable(事件表)和 EventQueue(事件队列)。

EventTable存储了宏任务的相关信息,包括事件监听和相应的回调函数。当特定类型的事件发生时,对应的回调函数被添加到事件队列中,等待执行。例如,你可以通过 addEventListener来将事件监听器注册到事件表上:

document.addEventListener('click', function() {
  console.log('Hello world!');
});

微任务与 EventQueue 密切相关。当执行栈中的代码执行完毕后,JavaScript引擎会不断地检查事件队列。如果队列不为空,就将队列中的事件一个个取出,并执行相应的回调函数。

601912c8532e75ea25db528fe787d544.jpeg

任务队列的执行流程可概括为:

  1. 同步任务在主线程排队执行,异步任务在事件队列排队等待进入主线程执行。

  2. 遇到宏任务则推进宏任务队列,遇到微任务则推进微任务队列。

  3. 执行宏任务,执行完毕后检查当前层的微任务并执行。

  4. 继续执行下一个宏任务,执行对应层次的微任务,直至全部执行完毕。

这个流程确保了异步任务能够在适当的时机插入执行,保持程序的高效性和响应性。

如果看到这里,还觉得有点懵,我们不妨看看下面这个示例解析,一定会让你清晰明了!!!

示例解析(很重要!这也是面试常考笔试题)

console.log(1);
setTimeout(() => {
    console.log(2);
}, 0);
console.log(3);
new Promise((resolve) => {
    console.log(4);
    resolve();
    console.log(5);
}).then(() => {
    console.log(6);
});
console.log(7);

执行顺序解析:1 => 3 => 4 => 5 => 7 => 6 => 2。

  1. 创建Promise实例是同步的,所以1、3、4、5、7是同步执行的。

  2. then方法是微任务,放入微任务队列中,在当前脚本执行完毕后立即发生。

  3. 同步任务执行完毕后,执行微任务队列中的微任务。

  4. 最后, setTimeout放入宏任务队列,按照先进先出的原则执行。

注意:出现 asyncawait,等价于 promisethen

总结

希望本文对你有所帮助,如果你有任何疑问、建议或者其他,欢迎在评论区留言。

关于本文 https://juejin.cn/post/7318619321421217832

公号文章分七类

随时都会有更新

程序员

  1. 真诚利他

  2. 一个30岁前端老社畜的人生经历

  3. 2023年中大厂面试经历分享,很可惜,但是没关系

  4. 给迷茫的朋友一点建议吧,主要是前端方向的。

  5. 37岁的老前端在大专院校教前端

  6. 一个30岁老前端的人生经历(学习+工作+婚姻+孩子),给迷茫的朋友一点激励。

  7. 程序员如何应对ChatGPT带来的改变

  8. 尤雨溪解读 2022 Web 前端生态趋势

  9. 阿里前端:我的老婆失业了,周围同事也在不断被裁

  10. 一个月薪 12000 的北京程序员的真实生活

  11. 作为前端,工作中处理过什么复杂的需求?

  12. 尤雨溪亲自回应Vue.js涉及国家安全漏洞问题

  13. 开源作者恶意搞破坏,谁来为开源买单?

  14. 程序员裸辞后,在家全职接单一个月的感触

  15. 2022年如何成为一名优秀的大前端Leader?

面试

  1. 14个JS面试难点深入解读与代码实现

  2. 中小型公司三年工作经验的面试经历

  3. 2023年中大厂面试经历分享,很可惜,但是没关系

  4. 面试官:能不能给 Promise 增加取消功能和进度通知功能... 我:???

  5. 一个22届被裁前端思想上得转变

  6. 23年底,两年前端菜狗被裁后的面试经历

  7. 一年空窗期后我是如何准备面试的?

  8. 一份比较完整的字节技术面试题,包含算法、计算机网络和前端等

  9. 面试官:请使用 JS 完成一个 LRU 缓存?

  10. 正确介绍自己的项目,终于不用害怕面试了

  11. 本人是工作 11 年的老前端,面试一个月忽悠了十几个 offer

JavaScript

  1. 14个JS面试难点深入解读与代码实现

  2. ES14数组升级来袭,这六个新API助你高效开发

  3. FaceBook 开源 AtomicCss 解决方案:Stylex

  4. 前端是怎么解析Excel、PDF、Word、PPT等文件的?

  5. 面试官:能不能给 Promise 增加取消功能和进度通知功能... 我:???

  6. 14个提高JavaScript代码质量的小技巧

  7. JS es6仿网易云音乐播放器

  8. WebSocket 从入门到入土

  9. 如何构建一个仅有2KB大小、无依赖的状态管理器(以及它如何帮我获得两个不同的工作机会)

  10. JS代码其实可以这样写

  11. 详解HTML中的拖拽案例和难点分析

  12. 20 个 JS 工具函数助力高效开发

  13. 使用 JavaScript 编写更好的条件语句

  14. JS 运行机制最全面的一次梳理

  15. 8个console.log的解决方案

  16. 25个有用的 JavaScript 单行代码

  17. 前端工程师都在忙些什么?

  18. 我用 80 行核心 JS 代码每个月躺着挣一瓶肥宅快乐水

  19. localStorage 的高阶用法

  20. 某一线前端小组长的 Code Review 分享

  21. 一行 Object.keys() 引发的思考

  22. 从浏览器多进程到JS单线程,JS运行机制最全面的一次梳理

  23. 常用的前端JavaScript方法封装

  24. 刷算法题常用的JS基础扫盲

  25. 2022前端应该掌握的10个 JS 小技巧

  26. Three.js实现跳一跳(在线玩)

  27. 10个常用的JS工具库,80%的项目都在用!

  28. 我的代码简洁之道

  29. 一行 Object.keys() 引发的血案

前端开发

  1. 聊一聊自己的前端之路以及后面晋升的一些想法

  2. 如何做好前端项目组组长

  3. 如何构建一个仅有2KB大小、无依赖的状态管理器(以及它如何帮我获得两个不同的工作机会)

  4. 高级前端开发工程师必备:Hooks、React Router v6 和状态管理

  5. 高级前端开发工程师必知:浏览器解析代码、JavaScript代码执行流程、原型链与闭包

  6. 高级前端开发工程师必备:Hooks、React Router v6 和状态管理

  7. 高级前端开发工程师必知:浏览器解析代码、JavaScript代码执行流程、原型链与闭包

  8. JS代码其实可以这样写

CSS

  1. CSS中的相对单位和绝对单位,以及rem自适应布局

  2. 10个常见渐变交互效果

  3. CSS动画的实现和最佳优化实践

  4. 现代CSS中的换行布局技术

  5. 你知道flex: 0 0 200px;和grid-template-columns: repeat(3, 1fr);的含义吗?

  6. 10 个不错的 CSS 小技巧

  7. 为什么会存在1px问题?怎么解决?

  8. 2022 年的 CSS 全览

  9. CSS mask 实现鼠标跟随镂空效果

AI

  1. 无代码工具+人工智能:19岁少年月入5000美元,八款免费工具助你在线赚钱!

  2. AI 时代来临,这些 AI 工具让你的工作更加高效!

  3. 程序员如何应对ChatGPT带来的改变

  4. 10个热门的ChatGPT项目推荐

  5. ChatGPT 8个场景下的灵活应用技巧,让您事半功倍!

资源

  1. 程序员必看!15个优秀的中文技术博客汇总

  2. AI 时代来临,这些 AI 工具让你的工作更加高效!

  3. 程序员如何应对ChatGPT带来的改变

  4. 10个热门的ChatGPT项目推荐

  5. 推荐15个有用的前端技术博客

  6. 尤雨溪解读 2022 Web 前端生态趋势

  7. 2022,VSCode 前端插件推荐

  8. 几个高级前端常用的API

  9. 30个前端开发人员必备的顶级工具

  10. 45 个 Git 经典操作场景,专治不会合代码

  11. 推荐 10 个很“哇塞”的Web“资源”给前端工友,收藏等于学会~

  12. 送给 xdm 的 10 个 web 在线前端资源,优雅永不过时~

  13. 干货!移动端真机调试指南,对调试说easy

  14. 25 个前端相关的学习网站和一些靠谱的小工具

最后

欢迎长按图片加好友,我会第一时间和你分享前端行业趋势,面试资源,学习途径等等。

4e0b040a87cac3e7a09eb0f94846bb2e.png

添加好友备注【加群】拉你进技术交流群

公众号前端开发博客 专注 前端开发技术,分享 前端开发资源WEB前沿资讯,如果喜欢我的分享,给 宝哥 点一个 或者 分享 都是对我的支持

关注公众号后,在首页:

  • 回复「小抄」,领取Vue、JavaScript 和 WebComponent 小抄 PDF

  • 回复「Vue脑图」获取 Vue 相关脑图

  • 回复「思维图」获取 JavaScript 相关思维图

  • 回复「简历」获取简历制作建议

  • 回复「简历模板」获取精选的简历模板

  • 回复「电子书」下载我整理的大量前端资源,含面试、Vue实战项目、CSS和JavaScript电子书等。

  • 回复「知识点」下载高清JavaScript知识点图谱

  • 回复「读书」下载成长的相关电子书

老规矩,学会了点个赞或在看呀~ 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值