JS中的事件循环机制
本文最后更新于 2024-12-28,文章内容可能已经过时。
事件循环
事件循环
主要是用来处理异步任务
的,一般异步任务
中的回调函数
会由另一个线程
处理完结果后会存放
到事件队列
中,等待js线程去处理
!
js线程
是单线程
,避免耗时任务
阻塞代码执行,会将耗时异步任务
推送给浏览器的另一个线程
去执行,当执行完毕时,会将任务中的回调函数
添加到事件队列
中,等待js线程
下次执行!
进程和线程
进程
和线程
之间是相互关联
的,且一个进程
可以包含多个线程!``进程
是运行程序
时所产生的进程
!线程
是每个进程
内部,所分配的资源任务
!
通俗点理解,就是
工厂
有多个车间
,其车间
内部有多个员工
在执行任务
! 其次车间
可以比喻为进程
,而员工
可以比喻为线程!
Js中的线程
JavaScript
是单线程
,且它的进程
归属于浏览器
或者Node
!浏览器
中有多个进程
,主要防止多个tab页
卡死未响应的问题!进程
中又包含多个线程
,其中包含js线程
!
js 单线程
意味着,同一个时间
只能做一件
事儿!非常耗时
的代码任务,会阻塞
其它代码的运行
!
非常耗时
: 比如有网络请求,定时器
等相关任务
!所以,这些
耗时的任务
,由其它线程
来执行,并非是JS线程
执行的任务!
例如: 有一个
网络请求
,会通知浏览器
向服务端发送请求
,一旦服务器返回响应
时,会将回调函数
添加到事件队列
中,且js线程
会从事件队列
中取出回调函数并执行!
事件循环队列
JavaScript是单线程的
,所以在执行一段代码
时,会从上往下
依次执行
,但是代码中难免会遇到特殊的代码,比如setTimeout
和网络请求
以及文件操作
等等相关比较耗时的代码
,因此,js单线程
只有等待耗时任务结束
后,才能继续执行
其他代码
任务! 所以为了避免这个问题
,这些耗时的任务
,通常由其它线程来完成
!例如
setTimeout
console.log("script start")
setTimeout(function (){
}, 1000)
console.log("script end")
setTimeout()
本身就是一个普通函数
,并不是什么异步函数
,只不过参数会接受一个回调函数
,通过规定的时间结束
后,才会执行回调函数
!
setTimeout
函数中的计时操作
,是交给浏览器其它线程
来完成的,另一个线程会保留回调函数
,并且进行计时操作
,当计时结束
后,就会将回调函数添加到事件队列
中,等js线程
将同步代码执行完毕
后,会从事件队列
里获取回调函数并执行
!
所以可以看出
事件循环
是有以下三个部分组成:js线程
->浏览器其它线程
->事件队列
js线程
会将耗时的任务
,添加到浏览器其它线程中执行,等待结果后
,会将回调函数
添加到事件队列
中,最终js线程执行完同步代码后,
会从事件队列
里获取回调函数并执行
!
宏任务和微任务
其实
事件队里
是一个总称,其中内部还划分为宏任务队列
和微任务队列
,两个队列会分别存放对应的任务回调函数
!
宏任务
:定时器,网络请求,dom事件监听
等属于宏任务
,并且回调函数
会添加到宏队列
中!
微任务
:Promise.then,queueMicroTask 还有 mutationObserver
等都属于微任务
,最终回调函数
会添加到微队列
中!
普通函数
普通函数
是不会放到队列
中执行的,当调用函数
时,会在调用栈中立刻执行
!
异步函数
异步函数
都有自己的回调函数
,执行任务时会交由其它线程
执行,之后会添加到队列
里,等待js线程
来调用!
执行顺序
执行宏任务之前
,一定是同步任务和微任务执行结束后
,才会执行宏任务
!确保
微任务队列中是空的时
,才会去宏任务队列
查看有没有需要执行
的任务!
js线程
:同步任务
->微任务
->宏任务