本文最后更新于 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线程: 同步任务 -> 微任务 -> 宏任务