生命周期图
timers--->pending callback--->idle perpare--->poll--->check--->close callbacks
| nextTick |
| Promise |
| <-----------------------------| |<------------------------------|
main---------------------------------->event loop?------------否-------------------->over
如何运行
1、首先运行的是mian启动入口文件
2、检查是否需要进行事件循环(event loop)
如何检查?它检查别的线程还有没有一些其他的事情没有完成或则说还有没有别的任务没有完成;
比如说还有个计时器,还有个文件还没有读完,看像这些事情有没有结束,没有完成就走一圈,
如果说所有的事情都没了,没有任何事情可以做和没有任何事情可以等待了,然后就over,整个程序就结束了
3、over整个程序结束
具体的事件循环是怎样的?
事件循环具体的流程
微队列
nextTick
Promise
宏队列
1、timers
2、pending callback
3、idle perpare
4、poll
5、check
6、close callbacks
我们主要关注的是宏队列里面的timers、poll和check,其他三个是关于操作系统的这里不做深究,我们可以把这些看成队列
timers:计时器队列 这里存放的是计时器里面回调函数
setTimeout(()=>{
console.log(1);
})
console.log(2)
//打印 2 1
//node执行,发现计时器,就把计时器里面的函数先放到timers里面,然后执行console.log(2),
//执行完后看事件队列中有没有事情可以做或则有没有事情被等待,一看timers里面有回调函数
//就执行 console.log(1);执行完后事件循环发现没有什么可以做了就over,程序结束,最终打印2,1
![](https://i-blog.csdnimg.cn/blog_migrate/8264b04b147b7d573254d9764ded43db.png)
poll:轮询队列 除了timers和checks绝大部分回调函数都会放入该队列,比如:文件读取,监听用户请求;
运作方式:如果poll中有回调函数,一次执行回调,直到清空队列,
如果poll中没有回调,则它会等待其他队列中出现回调,结束该阶段,
进入下一阶段,如果其他对垒也没有回调,持续等待,直到出现回调位为止
index.js文件
setTimeout(function f1(){
console.log('setTimeout');
},5000)
const http=require("http");
const server= http.createServer(()=>{
console.log("request in ")
})
server.listen(9527);
控制台使用node执行index.js文件
打印结果为:
setTimeout
//然后一直等在不结束?为什么呢
//具体运行流程:先运行入口模块,全局上下文还没什么东西,只是创建了一个http.createServer,当运行到server.listen(9527);
就看事件循环,看是否有事情或者有没有什么东西等待执行,先看计时器有没有回调(timers),
还没有计时器的回调【解释:应为计时器是5秒后才执行,运行到server.listen(9527)监听端口时还没有5秒,所有计时器的回调函数还不会放入】,
发现没有则去轮询队列(poll)中看有没有用户请求过来,发现也没有用户请求过来,所以就卡在这里不会继续运行,一直等,等一个回调函数到达其他的队列中去,
5秒过后计时器的f1函数来了,放在timers队列中进行等待执行,然后poll就结束等待,继续循环看check队列是否有回调函数发现没有继续循环,然后再看是否有事情
需要等待执行,发现timers队列中有f1函数等待执行,所以这时候就运行了f1函数,就输出了setTimeout,输出完后,继续到poll队列,然后继续看有没有用户请求过来
发现没有,这是就一直等待,除非有用户发送请求,否则就会一直等在,程序不会结束,所以才会出现打印setTimeout 就没了的结果
![](https://i-blog.csdnimg.cn/blog_migrate/559049055298569fffbaaab35138c60d.png)
check:检查阶段 使用setImmediate的回调会直接进入这个队列
setImmediate可以想象成setTimeout(()=>{},0)的时间为0的时候一样,立即执行;
timers队列是怎么检查是否有回调的呢?
它是检查计时器线程,一个计时器一个计时器拿出来看,看开始计时的时间,到当前时间,有没有到达计时的时间
【相当于:开始计时是10点0分,当前时间是10点5分,计时时间是10分钟,这显然还没到达】
当时间到达了之后就会拿出回调函数来执行,她内部也会进行排序运行算
check这里就没什么检查,这里就是一个真实的队列,而timers其实不是一个对列,为了方便记忆才说是队列;
一旦调用setImmediate,就会直接把回调函数丢到check队列中去,到了就直接执行,所以check队列运算时间是很少的
let i=0;
console.time()
function test(){
i++;
if(i<1000){
setTimeout(test,0)
}else{
console.timeEnd()
}
}
test()
//运行结果为:1.352s;运行了1秒多;为什么运行这么久呢
//主函数运行,发现有一个test()函数执行,执行代码开了一个计时器,把test函数放到计时队列中,poll发现其他队列有回调函数就结束,继续事件循环
//循环一圈后检查,有没有计时器,发现有,然后看一下这个计时器的时间到没到,发现是0秒,可能到了,就运行test函数,运行完后发现又创建了一个计时器
//就又再timers队列后面加一个计时器,然后之前的计时器就运行完了,这个计时器就等在被执行,poll看到又有队列又函数被等待执行,就结束,继续循环,
//就这样一直循环等在执行,为什么时间会这么旧就是因为,就是因为每一个计时器都会去计算看开始计时的时间,到当前时间,有没有到达计时的时间
let i=0;
console.time()
function test(){
i++;
if(i<1000){
setImmediate(test)
}esle{
console.timeEnd()
}
}
test();
//结果:5.284ms;结果快了很多
//因为setImmediate是直接加入到check队列中的,到了check队列就直接运行,没有什么算时间有没有到的问题
![](https://i-blog.csdnimg.cn/blog_migrate/8c96f28e76e4d6170e7486ede9acf3e9.png)
面试题来了:下面结果输出什么
1、
setTimeout(()=>{
console.log("setTimeout")
},0);
setImmediate(()=>{
console.log("setImmediate")
});
//从事件循环顺序来看,感觉上是先输出setTimeout,然后再输出setImmediate,其实不一定
//其实setTimeout是取不到0的,至少都是1,就有可能会出现一种情况,我计算机很好运行很快
//就是从我们计时开始到事件循环检查是否有没有到时间的时候还没到一毫秒,那么计时器队列就是为空的
//就继续运行到poll,然后发现check里面有东西了,就结束等待,继续循环,到了check队列,然后就输出setImmediate
//到了一秒后就把计时器函数放到计时器队列中然后就执行函数打印setTimeout
//另一种情况就是,运行发现setTimeout回调函数放入时间队列,setImmediate回调函数翻入check队列,这时我计算机有点卡,
然后检查时间队列的时候发现时间已经到了1ms,就直接运行时间队列的回调函数,最后运行check队列,所以结果课程可能是setTimeout setImmediate
2、
const fs= require("fs");
fs.readFile("./index.js",()=>{
setTimeout(()=>console.log(1));//这里默认时间时0
setImmediate(()=>console.log(2));
})
//这里打印的结果永远是2,1
//运行入口文件,进入事件循环,发现时间队列没有值就运行poll,poll发现其他队列都没有回调函数,就卡在这里等待
//等待文件执行完,执行完后执行回调函数,发现有setTimeout就把计时器函数放入时间队列,然后继续发现有setImmediate
//就把回调函数放入check队列中,到这里回调函数执行完,poll发现其他队列有事情需要做,就结束等待,让其他队列执行,
//这是到了check队列,就立马执行得到2,执行完后继续事件循环发现,时间队列中有回调函数,且发现开始计时的时间,
//与当前时间对比发现到了,就执行回调函数,要是没到就继续事件循环,直到计时时间到了为止,才执行回调函数得到值1
//所以为什么打印值一直时21
![](https://i-blog.csdnimg.cn/blog_migrate/3a246863e3c67d7f62a0c566abee3095.png)
![](https://i-blog.csdnimg.cn/blog_migrate/e52238a6ea0a36b0240c5424055b424c.png)
按宏队列与微队列来区分,上面将的都是宏队列微队列中有nextTick和Promise
事件循环中,每次打算执行一个回调函数前,必须要先清空微队列nextTick和Promise;
nextTick和Promise哪个先执行呢?nextTick一定比Promise快