浅谈个人对JavaScript Event Loop的理解

179 阅读4分钟

本文是对于以前学习Event Loop(事件循环)的一些个人的理解与总结。

首先我们要知道的是Javascript是一门单线程语言,单线程就意味着当程序开始运行时,每个任务都得排队,前一个任务结束才会执行下一个任务。目前市面上的CPU计算力是非常强的,单单就一点点计算量其实很快就能执行完,但是因为单线程的关系会导致cpu不得不等待例如Ajax等耗时操作才能往下执行。

于是JS的开发者为了解决这个问题就将任务分为同步任务和异步任务。同步任务指的是在主线程上排队执行的任务,异步任务指的是不进入主线程而直接进入事件队列的任务,只有事件队列通知主线程,hey boy我这里的异步任务可以执行了,这个任务才会进入主线程执行。

只要主线程空了,就会去读取"事件队列",这就是JavaScript的运行机制。这个过程会不断重复。

Event Loop 浏览器

宏任务和微任务 

事件循环中并非只维护着一个队列,事实上是有两个队列:

1.宏任务队列(macrotask queue):ajax、setTimeout、setInterval、DOM监听、UI Rendering等

2.微任务队列(microtask queue):Promise的then回调、 Mutation Observer API、queueMicrotask()等

事件循环对于两个队列的优先级

1.main script中的代码优先执行(编写的顶层script代码)

2.在执行任何一个宏任务之前(不是队列,是一个宏任务),都会先查看微任务队列中是否有任务需要执行.

  • 也就是宏任务执行之前,必须保证微任务队列是空的

  • 如果不为空,那么就优先执行微任务队列中的任务(回调) 

主线程从事件队列中读取事件,这个过程是循环不断的,这个运行机制又称为Event Loop.

通过上述理论我们就可以得知settimeout()方法的真正表述:

 setTimeout(() => {    console.log('轮到我了吗')  }, 1000);
上述代码,在我们没有理解Event Loop的时候可能会误以为 
当程序执行的时候过一秒打印我们的console.log
但其实并不正确 因为如果setTimeout方法的前后含有大量的耗时代码 可能console.log10秒以后打印
也说不一定的
例如:
function fn(){
    // 这里有一百万条代码要执行  总共需要20秒
    setTimeout(() => {    console.log('轮到我了吗')  }, 1000);
    // 这里也有一百万条代码要执行  总共需要20秒
}
所以最终打印轮到我了吗 实际上是41秒以后  并不是fn函数执行后一秒打印哦
 
同样的道理:
function fn(){
    setTimeout(() => {    console.log('轮到我了吗')  }, 0);
    这里有一百万条代码要执行  总共需要20秒
}
虽然延迟时间设置为0,但它终究还是异步操作,JS会优先执行同步操作后马上执行打印
所以是20秒之后才打印轮到我了吗

面试题练习

Event Loop Node

在Node.js中的EVENT LOOP 与浏览器有许多异同点

事件循环像是一个桥梁,是连接着应用程序的JavaScript和系统调用之间的通道

1. 无论是我们的文件IO、数据库、网络IO、定时器、子进程,在完成对应的操作后,都会将对应的结果和回调函 数放到事件循环(任务队列)中; 

2. 事件循环会不断的从任务队列中取出对应的事件(回调函数)来执行

但是一次完整的事件循环Tick分成很多个阶段

  • 定时器(Timers):本阶段执行已经被 setTimeout() 和 setInterval() 的调度回调函数。 
  • 待定回调(Pending Callback):对某些系统操作(如TCP错误类型)执行回调,比如TCP连接时接收到 ECONNREFUSED。
  •  pidle, prepare:仅系统内部使用。 
  • 轮询(Poll):检索新的 I/O 事件;执行与 I/O 相关的回调; 
  • 检测(check):setImmediate() 回调函数在这里执行。
  •  关闭的回调函数:一些关闭的回调函数,如:socket.on('close', ...)

Node的宏任务和微任务 

  1. 宏任务(macrotask):setTimeout、setInterval、IO事件、setImmediate、close事件; 
  2. 微任务(microtask):Promise的then回调、process.nextTick、queueMicrotask;

但是,Node中的事件循环不只是 微任务队列和 宏任务队列: 

  •  微任务队列:  

next tick queue:process.nextTick;

other queue:Promise的then回调、queueMicrotask; 

  •  宏任务队列: 

 timer queue:setTimeout、setInterval

 poll queue:IO事件; 

 check queue:setImmediate; 

 close queue:close事件; 

Node事件循环的顺序 

next tick microtask queue; 

pother microtask queue; 

ptimer queue;

ppoll queue;

pcheck queue;

pclose queue;

面试题