Event Loop——详解

340 阅读4分钟

Event Loop是什么

  • Event Loop是浏览器的一种运行机制,采用这种机制,来解决JavaScript单线程运行带来的问题

JavaScript单线程

  • 进程:运行以后的程序叫做"进程"。一个进程一次只能执行一个任务。
  • 线程:它被包含在"进程"之中,是"进程"中的实际运作单位,一个进程中可以并发多个线程,每条线程并行执行不同的任务
  • JavaScript从诞生起就是单线程。原因大概是不想让浏览器变得太复杂,因为多线程需要共享资源、且有可能修改彼此的运行结果,对于一种网页脚本语言来说,这就太复杂了。后来就约定俗成,JavaScript为一种单线程语言
  • JavaScript代码是在浏览器的渲染进程(Renderer Process)里的JS引擎线程(单线程)执行的
  • 所有的 JS 代码其实都由 renderer Process 控制的,所以在你浏览网页内容的过程大部分时候不会涉及到其它的进程

没有Event Loop机制

  • 单线程的弊端在于,一旦前面遇到大量任务或者遇到一个耗时的任务,网页就会出现"假死",因为JavaScript停不下来,也就无法响应用户的行为,
  • 在实现交互时,无法避免,当某个任务很耗时,比如涉及很多I/O(输入/输出)操作,此时线程运行如下:

image.png 这样将闲置资源,显然是不合理的。Event Loop就是为了解决这个问题而提出的

有Event Loop机制

  • 需要注意的是,Event Loop机制不是一直存在在线程中,是当主线程的所有同步任务执行完毕后,系统就会读取"任务队列",读取任务栈里面的异步任务,只有"任务队列"通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。重复以上操作
  • 主线程重复寻找任务队列的异步任务,可以用一张图说明一下流程

image.png

  • 那么Event Loop机制是如何处理主线程和其他线程的通信,达到资源充分利用的呢? 首先我们先来认识一下Event Loop中重要的概念:任务队列,同步任务和异步任务

任务队列

JavaScript中,有两类任务队列:宏任务队列(macrotask ),微任务队列(microtask )。宏任务队列可有多个,微任务只有一个。
- 宏任务:script(主代码块),setTimeoutsetInterval,I/O,UIrendering
- 微任务:Promise,Object.oberver,process.nextTick(node.js进程中的相关对象),MutationObserver

同步任务和异步任务

JavaScript单线程中,任务分为同步任务和异步任务。同步任务会在调用栈中按照顺序排队等待主线程执行,异步任务则会在异步有了结果后将注册的回调函数添加到任务队列(消息队列)中等待主线程空闲的时候,也就是栈内被清空的时候,被读取到栈中等待主线程执行。任务队列是先进先出的数据结构

  • 同步任务:可以立即执行的任务,例如声明一个变量或者执行一次加法操作等。同步任务属于宏任务
  • 异步任务:是不会立即执行的事件任务。异步任务包括宏任务和微任务

现在我们可以看一段代码理清一下

console.log("script start")
setTimeout(function(){
    console.log('timeout1')
},10);
new Promise(resolve=>{
    console.log("promise1");
    resolve();
    setTimeout(()=>{
        console.log('timeout2')
    },10);
}).then(function(){
    console.log("then1")
})
console.log("script end")

首先,先将同步任务执行,即输出script start,遇到异步任务setTimeout1,放入任务队列中,继续往下,执行同步任务,输出promise1, resolve()为异步任务,放入任务队列,往下遇到setTimeout2,放入任务队列中,往下遇到同步任务,输出script end。先同步任务全部执行完毕,去任务队列,先查看有无可执行的微任务,有pormise,故将该任务放入主线程中执行,输出then1,无可执行的微任务,查看有无可执行的宏任务,有setTimeout1,故输出timeout1,有无可执行的微任务,无则去查看有无可执行的宏任务,有setTimeout2,故输出timeout2 执行结果如下:

script start
promise1
script end
then1
timeout1
timeout2

总结

事件循环机制是发生在浏览器渲染进程中js引擎线程(主线程)和事件触发线程的之间的,然后任务队列是放在事件触发线程,然后调用栈是放在主线程。js引擎先执行完调用栈的同步任务,异步任务放到任务队列,调用栈执行完毕,去任务队列寻找可以调用的任务(先调用微任务,在调用宏任务),放到调用栈中,执行这个异步任务在js引擎的回调函数。(粗体为Event Lop事件循环),我画了如下图总结这段话

image.png