JavaScript事件循环机制的深入理解

 更新时间:2025年10月20日 11:07:55   作者:flower_tomb  
JavaScript是一种单线程语言,依赖于事件循环机制,完成对同步和异步任务的处理,从而实现非阻塞并发,这篇文章主要介绍了JavaScript事件循环机制的相关资料,需要的朋友可以参考下

首先,我们要理解JavaScript是一门单线程的语言。所谓单线程,简单来说一个时间只能做一件事,只有做完这件事,才能进行下一件。那为什么选择单线程,不选择多线程呢?这是由JS的用途决定的,JS的用途是与用户交互,以及操作DOM,假设JS有两个线程,一个要在某个DOM节点上添加内容,一个要删除这个节点,那浏览器该以哪个为准呢,事情就变得复杂了。因此,JS在诞生时就是单线程,以后也不会改变。

这个时候就出现了问题,如果一件任务耗时太长,就会阻塞后面的任务。这样肯定是不行的,所以,JS设计者将任务分成同步任务和异步任务。

同步任务就是在主线程(调用栈 Call Stack)中按照书写顺序依次执行的任务。异步任务就是不阻塞主线程,而是交由其他线程或系统处理(如浏览器 或 Node.js ),处理完毕后,将其回调函数推入任务队列。等到主线程空了,任务队列中的任务再根据FIFO算法被调度到主线程中执行。

事件循环的工作流程如下:

1.执行调用栈中的所有同步代码,直到栈空。【栈是后进先出结构,但是同步代码入栈后直接执行,执行完毕弹出,所以顺序还是不变的,但如果函数嵌套,则调用栈可能存在多层函数上下文,最后进入的上下文最先执行完,所以最先弹出】
2.检查任务队列,依次将任务调度到调用栈中执行,直到队列空。
3.重复循环。

只要调用栈空了,就去任务队列中读取任务,【用户点击按钮触发函数,函数就会被推入调用栈执行】这个过程不断重复循环,这就是JavaScript的运行机制, 这种机制就叫做事件循环机制

所以有一个小细节就是setTimeout(callback,s)的真正含义并不是在指定的毫秒数后调用函数,而是最快s毫秒后调用函数,因为它需要等待主线程空后再被调用。

但是这里面还有更细节的问题,在事件循环的早期设计中,所有异步任务都进入同一个任务队列。但随着前端复杂度提升,任务优先级问题显露出来:

紧急任务需要优先处理(如 Promise 状态更新)
非紧急任务可以延后(如 UI 渲染前的计算)

因此,现代事件循环又将异步任务细分为两类:

同步任务与宏任务:整个脚本(主线程代码),即script标签里的代码,本身就是一个宏任务,主线程执行脚本中的同步代码属于初始宏任务。整个脚本的执行是第一个宏任务,同步代码是它的组成部分。

于是,事件循环的工作流程就变成:

1.按照代码书写顺序执行初始宏任务:

如果遇到同步代码,直接推入调用栈执行。
如果遇到宏任务,将回调函数推入宏任务队列。
如果遇到微任务,将回调函数推入微任务队列。

2.清空微任务队列:

当前宏任务执行完毕后,依次将微任务队列中的所有微任务推入调用栈执行,直到微任务队列清空。
注意:若微任务中又生成新的微任务,新微任务也会在此阶段被立即执行。

3.渲染更新(如有必要):

浏览器判断是否需要渲染(通常根据屏幕刷新率,如 60Hz 对应约 16.6ms/次)。

4.开启下一轮事件循环:

从宏任务队列中取出下一个宏任务执行,重复上述流程。

Tips:我的一些思路历程

刚开始我以为js在遇到settimeout这种定时器时,会将整个定时器函数推到宏任务队列中,但其实不是的,
【如果是这样的话,那一个3秒的宏任务,会和一个耗费8秒的微任务几乎同时分别推入宏任务队列和微任务队列,那这个时候如果调用栈执行空了,先取出这个微任务执行,那可能8s的打印出来了,定时器还没打印出来,这个深究的话这个理解是错误的,不过可以帮我记住任务队列里放的都是处理过的回调

js遇到定时器之后,将定时器以及回调交给浏览器,由浏览器的定时模块发起定时,处理完毕后(例如三秒后)将回调函数推入宏任务队列。

接着列举一些宏任务和微任务的理解,以及他们是怎么分类的

宏任务:

由宿主环境发起,比如浏览器或者Node。js遇到这些任务时会向对应的环境发起请求,由浏览器或者node进行处理,处理完毕后推入宏任务队列

‌浏览器环境‌: setTimeout、setInterval【定时器触发线程】、DOM事件【事件触发线程、GUI渲染线程】、AJAX回调【浏览器网络线程】等需要浏览器线程协作的任务
‌Node环境‌: I/O操作、setImmediate等系统级异步操作

微任务:

由JavaScript引擎自身发起和管理【相当于是一种js本身的异步机制】,例如js遇到promise的时候,会执行promise的同步部分,然后将promise的状态标记为pending,而直到当resolve/reject被调用时,js才会将对应的.then或者.catch回调函数推入微任务队列

‌语言级实现‌:Promise.then/catch/finally、queueMicrotask等属于ECMAScript规范定义的异步机制

到此这篇关于JavaScript事件循环机制的文章就介绍到这了,更多相关js事件循环机制内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Avalon中文长字符截取、关键字符隐藏、自定义过滤器

    Avalon中文长字符截取、关键字符隐藏、自定义过滤器

    avalon是一个简单易用迷你的MVVM框架。通过本文给大家介绍Avalon中文长字符截取、关键字符隐藏、自定义过滤器的相关资料,需要的朋友一起学习吧
    2016-05-05
  • 详细聊聊JavaScript是如何影响DOM树构建的

    详细聊聊JavaScript是如何影响DOM树构建的

    DOM (Document Object Model) 的全称是文档对象模型,它可以以一种独立于平台和语言的方式访问和修改一个文档的内容和结构,这篇文章主要给大家介绍了关于JavaScript是如何影响DOM树构建的相关资料,需要的朋友可以参考下
    2021-08-08
  • js中几种循环的退出方式实例总结

    js中几种循环的退出方式实例总结

    提到在一段程序中如果碰到需要终止,结束一个循环,函数或者一段代码,一般会想到以下这几个关键字return、continue、break,这篇文章主要给大家介绍了关于js中几种循环的退出方式,需要的朋友可以参考下
    2022-12-12
  • 认识延迟时间为0的setTimeout

    认识延迟时间为0的setTimeout

    由 John Resig 的 How JavaScript Timers Work 可以知道,现有的 JavaScript 引擎是单线程处理任务的。它把任务放到队列中,不会同步去执行,必须在完成一个任务后才开始另外一个任务。
    2008-05-05
  • JavaScript数组常用方法find、findIndex、filter、map、flatMap及some详解

    JavaScript数组常用方法find、findIndex、filter、map、flatMap及some详解

    在JavaScript中数组是一种非常常见且功能强大的数据结构,这篇文章主要介绍了JavaScript数组常用方法find、findIndex、filter、map、flatMap及some的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下
    2025-07-07
  • javascript createAdder函数功能与使用说明

    javascript createAdder函数功能与使用说明

    createAdder(x)是一个函数,返回一个函数。在JavaScript中,函数是第一类对象:另外它们可以被传递到其他函数作为参数和函数返回。在这种情况下,函数返回本身就是一个函数接受一个参数,并增加了一些东西。
    2010-06-06
  • Firefox下提示illegal character并出现乱码的原因

    Firefox下提示illegal character并出现乱码的原因

    Firefox下提示illegal character并出现乱码的问题,时间是是因为编码的问题导致。
    2010-03-03
  • 使用控制台破解百小度一个月只准改一次名字

    使用控制台破解百小度一个月只准改一次名字

    这篇文章主要介绍了使用控制台破解百小度一个月只准改一次名字的方法和代码,有需要的小伙伴可以参考下。
    2015-08-08
  • JS实现DIV容器赋值的方法

    JS实现DIV容器赋值的方法

    这篇文章主要介绍了JS实现DIV容器赋值的方法,可结合ajax使用,涉及JavaScript针对页面元素的动态操作技巧,需要的朋友可以参考下
    2015-12-12
  • 用JS实现根据当前时间随机生成流水号或者订单号

    用JS实现根据当前时间随机生成流水号或者订单号

    本文通过实例代码给大家介绍了基于JS实现根据当前时间随机生成流水号或者订单号的相关知识,非常不错,具有一定的参考借鉴价值,需要的朋友参考下吧
    2018-05-05

最新评论