javascript技巧

关注公众号 jb51net

关闭
首页 > 网络编程 > JavaScript > javascript技巧 > js事件循环机制

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

作者: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事件循环机制内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文