"深入理解 JavaScript 中的事件循环(Event Loop)机制"

前言

事件循环(event loop)是一种用于处理异步操作的编程模型。在 JavaScript 中,事件循环是指浏览器或 Node.js 运行时环境中负责管理执行顺序的机制。

简单来说,事件循环不断地监听事件队列,当事件队列中有任务时,会从中取出一个任务并执行。执行完当前任务后,再次检查事件队列,如此循环,直到事件队列为空。

这种机制使得 JavaScript 能够处理异步操作,例如定时器、网络请求、事件监听等,而不会阻塞主线程的执行。这样就实现了非阻塞的异步编程,提高了程序的性能和响应速度。

进程与线程

首先我们来了解一下什么是进程什么是线程。

进程是操作系统中的一个基本概念,它表示正在运行的一个程序的实例。每个进程都有自己独立的地址空间,包括代码、数据、堆栈等,同时还包括了进程所需的一些系统资源,比如文件描述符、信号处理设置等。进程之间一般是相互独立的,彼此不会影响。

线程是进程中的一个实体,是 CPU 调度和分派的基本单位。一个进程可以包含多个线程,这些线程共享进程的地址空间和系统资源,但每个线程有自己的调用栈和局部变量。多线程使得程序可以进行并发执行,提高了程序的效率和响应速度。

总结起来,进程是程序的执行实例,包含了程序的代码、数据和系统资源;而线程是进程中的执行单元,多个线程可以共享进程的资源,实现并发执行。

异步与同步

同步操作是指程序按照顺序依次执行,每个操作都要等待上一个操作完成后才能进行。换句话说,当一个操作在执行时,程序会阻塞,直到该操作完成后才会执行下一个操作。这种执行方式简单直观,但可能会导致程序的响应速度变慢,特别是在处理大量耗时操作时。

异步操作是指程序中的操作可以同时进行,不需要等待上一个操作完成才能进行下一个操作。异步操作通常会通过回调函数、Promise 或者 async/await 等机制来处理。这种执行方式可以提高程序的效率和响应速度,特别适合处理大量的I/O操作或网络请求。

宏任务与微任务

在异步中,又包括宏任务与微任务。

宏任务:script,setTimeout,setInterval,setImmediate,I/O,UI-rendering(ui渲染)

微任务:promise.then(),MutationObserver

Event-Loop执行顺序

1.执行同步代码(这属于宏任务)

2.当执行栈为空时,查询是否有异步需要执行

3.如有则执行微任务

4.如果有需要则会渲染页面

5.执行宏任务(下一次event loop的开始)

下面我们通过一段代码来更好的理解event-loop的执行顺序。

js 复制代码
console.log("stard")           //第一个输出stard
async function async1(){     //async 相当于new Promise,构造函数属于同步代码
    await async2()
    console.log("saync1 end");    !!!第一个进入微任务队列
}
async function async2(){
    console.log("saync2 end");  //第二个输出saync2 end
}
async1()
setTimeout(function(){      !!!第一个进入宏任务队列
    console.log("setTimeout");
},0)
new Promise((resolve)=>{
    console.log("promise");  //第三个输出promise
    resolve()
})
.then(()=>{                 !!!第二个进入微任务队列
    console.log("then1");
})
.then(()=>{                 !!!第三个进入微任务队列
    console.log("then2")
})
console.log("end");      //第四个输出end

--------------------------------------------------------------------------------------------------------------- 首先我们按照步骤来先执行同步代码(第一行,输出stard ),往下运行,async1和async2的函数声明不用运行,运行到第九行async1(),因为async相当于new Promise构造函数,属于同步代码,虽然await相当于.then的用法,但浏览器默认直接运行await所在语句,且await后一行代码会进入微任务队列(系统会创建两个队列,一个宏任务队列,一个微任务队列,先进先出原则)

直接执行await async2(),第二个输出saync2 end,此时运行到第十行,因为setTimeout属于宏任务,所以第一个进入宏任务队列

继续运行到第十三行,new Promise 构造函数属于同步代码,所以直接运行,第三个输出promise

继续运行到第十七行,(promise.then)属于微任务,所以先后两个.then都进入微任务

继续运行到第二十三行,同步代码直接运行,所以第四个输出end。

--------------------------------------------------------------------------------------------------------------- 此时同步代码执行结束,开始执行微任务,第一个进入微任务队列的是第四行代码,所以第五个输出saync1 end ,第二、三进入微任务的是两个.then,所以第六个输出的是then1,第七个输出的是then2 ---------------------------------------------------------------------------------------------------------------

此时异步中微任务队列也全部执行完毕,开始执行宏任务,而宏任务中只有第十行setTimeout,所以第八个输出的是setTimeout。

此时我们对event-loop的讲解就接近尾声了,那么现在有个疑问,event-loop中微任务一定比宏任务先执行嘛?

答案是错误的,因为第一步执行的同步代码也属于宏任务,所以简单来说,一次event-loop是由宏任务开始的也是由宏任务结束的


相关推荐
uhakadotcom1 小时前
视频直播与视频点播:基础知识与应用场景
后端·面试·架构
范文杰2 小时前
AI 时代如何更高效开发前端组件?21st.dev 给了一种答案
前端·ai编程
拉不动的猪2 小时前
刷刷题50(常见的js数据通信与渲染问题)
前端·javascript·面试
拉不动的猪2 小时前
JS多线程Webworks中的几种实战场景演示
前端·javascript·面试
FreeCultureBoy3 小时前
macOS 命令行 原生挂载 webdav 方法
前端
uhakadotcom3 小时前
快速开始使用 n8n
后端·面试·github
uhakadotcom3 小时前
Astro 框架:快速构建内容驱动型网站的利器
前端·javascript·面试
uhakadotcom3 小时前
了解Nest.js和Next.js:如何选择合适的框架
前端·javascript·面试
uhakadotcom3 小时前
React与Next.js:基础知识及应用场景
前端·面试·github
uhakadotcom3 小时前
Remix 框架:性能与易用性的完美结合
前端·javascript·面试