前言
事件循环(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是由宏任务开始的也是由宏任务结束的