本文是对于以前学习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.log要10秒以后打印
也说不一定的
例如:
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的宏任务和微任务
- 宏任务(macrotask):setTimeout、setInterval、IO事件、setImmediate、close事件;
- 微任务(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;