说说事件循环Event Loop?Node事件循环和浏览器事件循环的区别?

144 阅读3分钟

image.png

一、什么是事件循环

主线程(事件循环):执行宏任务/微任务,生成 style tree,生成 layout tree,执行复合操作( Compositior )render tree,render tree 再由渲染线程异步地提交 GPU 渲染,查看事件队列,继续执行下一个宏任务

二、js执行机制

要知道事件循环,先知道js的执行机制

  1. 首先判断js任务是同步的是还是异步,同步任务会在主线程栈中依次顺序执行
  2. 异步任务会提交到异步进程处理,异步进程会把异步任务放到任务队列中
  3. 当主线程的,所有同步任务执行完后,会去任务队列中查看是否有可以执行的异步任务,如果有,拿到主线程中执行

事件循环,就是将异步任务反复从任务队列中,拿到主线程上执行的过程

三、异步任务

异步任务分为 宏任务(macrotask) 与 微任务 (microtask),

当执行完同步代码之后,就会执行位于执行列表之后的微任务,然后再执行事件列表中的宏任务

执行步骤

·  1.进入到script标签,就进入到了第一次事件循环.

·  2.遇到同步代码,立即执行

·  3.遇到宏任务,放入到宏任务队列里.

·  4.遇到微任务,放入到微任务队列里.

·  5.执行完所有同步代码

·  6.执行微任务代码

·  7.微任务代码执行完毕,本次队列清空

·  8.寻找下一个宏任务,重复步骤1

宏任务 -> 微任务队列 -> 渲染 -> 下⼀个宏任务 -> 下⼀个微任务队列 ...

浏览器中的事件循环

浏览器端事件循环中的异步队列有两种:macro(宏任务)队列和 micro(微任务)队列。

宏任务队列可以有多个,微任务队列只有一个

  • 常见的宏任务:setTimeout、setInterval、script(整体代码)、I/O 操作、UI 渲染等。

  • 常见的微任务: new Promise().then(回调)、MutationObserver(html5新特性) 等。

过程

当某个宏任务执行完后,会查看是否有微任务队列。如果有,先执行微任务队列中的所有任务,如果没有,会读取宏任务队列中排在最前的任务,执行宏任务的过程中,遇到微任务,依次加入微任务队列。栈空后,再次读取微任务队列里的任务,依次类推。

例如:

Promise.resolve().then(()=>{ 
    console.log('Promise1') 
    setTimeout(()=>{ 
        console.log('setTimeout2') 
},0) }) 
setTimeout(()=>{ 
    console.log('setTimeout1') 
    Promise.resolve().then(()=>{ 
        console.log('Promise2') }) 
},0)
// 结果:Promise1,setTimeout1,Promise2,setTimeout2

结果分析:

  • 一开始执行栈的同步任务(这属于宏任务)执行完毕,会去查看是否有微任务队列,上题中存在(有且只有一个),然后执行微任务队列中的所有任务输出Promise1,同时会生成一个宏任务 setTimeout2

  • 然后去查看宏任务队列,宏任务 setTimeout1 在 setTimeout2 之前,先执行宏任务 setTimeout1,输出 setTimeout1

  • 在执行宏任务setTimeout1时会生成微任务Promise2 ,放入微任务队列中,接着先去清空微任务队列中的所有任务,输出 Promise2

  • 清空完微任务队列中的所有任务后,就又会去宏任务队列取一个,这回执行的是 setTimeout2

Node中的事件循环

Node.js的运行机制如下:

  • V8引擎解析JavaScript脚本。
  • 解析后的代码,调用Node API。
  • libuv库负责Node API的执行。它将不同的任务分配给不同的线程,形成一个Event Loop(事件循环),以异步的方式将任务的执行结果返回给V8引擎。
  • V8引擎再将结果返回给用户。

浏览器事件循环与node事件循环的区别

两者最主要的区别,浏览器中的微任务是在每个相应的宏任务中执行的,而nodejs中的微任务是在不同阶段之间执行的。

image.png