在上期发表的博客中,我们详细讲述了一下JavaScript中的事件循环,我突然又想起来,NodeJS也是基于JavaScript语言的一种后台语言。那么他的事件循环跟Javascript中的事件循环有什么相同点又要什么区别呢?今天我们带着这样的疑问也来看看nodejs中的事件循环吧!
1.事件循环
如上图,大概就是nodejs中的事件循环基本流程。在进入后判断代码是否都为同步代码,如果都为同步代码,则不进入事件循环,直接结束。如果有异步代码,会进入相应的流程队列中。现在就让我们来介绍一下NodeJs中的几种主要的队列的功能和作用吧!
1. 1 timers队列
timers队列是存放计时器的回调函数队列,如setTimeout和setIterval都会进入这个队列。等到计时器时间到达之后,会在poll队列中执行回调(注意,setImmediate不进入timers队列!)
1.2 poll轮询队列
除了timers、checks,绝大部分回调都会放入该队列,比如:文件的读取、监听用户请求等
如果poll中有回调,依次执行回调,直到清空队列。如果poll中没有回调,等待其他队列中出现回调,结束该阶段,进入下一阶段,如果其他队列也没有回调,持续等待,直到出现回调为止
1.3 check检查队列
使用setImmediate的回调会直接进入这个队列
1.4 nextTick 和 Promise
nextTick:队列中的超级VIP,在事件循环中,每次打算执行一个回调之前,如计时器到时间了,要执行计时器中的回调了,但是不能直接执行,要看看nextTick队列中有没有还没有执行完的回调,如果有就先把nextTick中的执行完了,知道里面没有东西可以执行了,再来执行其他的回调。举个通俗点的例子,就比如你去银行取钱,但是那个银行只有一个窗口。银行有金卡VIP用户,普通用户必须等金卡用户们都取走钱之后,他才能去取钱,不然是取不到钱的。如果有新的VIP用户来,普通用户就必须还得等着,直到VIP金卡用户取完了,实在没有了。再去取钱。
Promise:跟nextTick一样,队列中的VIP,只是优先级没有nextTick高。这里就不多做重述了。
2.实际案例
2.1demo1
const fs = require("fs");
fs.readFile("./index.js", () => {
setTimeout(() => console.log(1), 0);
setImmediate(() => console.log(2));
});
在上面这个简单案例中,先来看看有没有同步代码,只有一个readFlie读取,直接看内部,有一个setTimeout计时器和一个setImmediate,这两个都是异步操作,直接进事件循环,上面我们讲过,setTimeout直接进入timers队列,而setImmediate比较特殊,他直接进入poll队列,等setTimeout到时之后,poll队列才会执行里面的回调。所以在这里setImmediate肯定比setTimeout先执行,所以先执行输出2,再执行输出1。让我们来看看结果吧!
2.2demo2
setImmediate(() => {
console.log(1);
});
process.nextTick(() => {
console.log(2);
process.nextTick(() => {
console.log(6);
});
});
console.log(3);
Promise.resolve().then(() => {
console.log(4);
process.nextTick(() => {
console.log(5);
});
});
小伙伴们已经理解了第一个简单案例的话,那让我们来一个更加复杂一点的案例来看看吧!让我们一起来分析分析,答案不就是呼之欲出吗。
首先在代码块中寻找同步代码,我们看到只有一个输出3是同步的。所以我们主线程先输出3是毫无疑问的。再来看异步代码。我们上面说过nextTick是事件循环中的超级VIP,所以先看nextTick,在nextTick里又分了同步和一个nextTick队列,所以当然先输出2,再输出6。这个nextTick代码块执行完成之后,再来看,我们说过Promise是事件循环中的VIP,等到nextTick执行完之后,就看他了。所以我们来看Promise代码块。里面又分了同步输出4和nextTick输出5,所以毫无疑问先输出4再输出5。最后还剩一个setImmediate,等到nextTick和Promise里面的东西都执行完之后,终于他才能执行了。所以我们的答案应该是326451。让我们来看看输出结果跟我们分析的一不一致吧!
太棒了!答案完全一致!如果大家分析这题没有问题的话!那我们再来看看更复杂一点的吧!
3.demo3
async function async1() {
console.log("async1 start");
await async2();
console.log("async1 end");
}
async function async2() {
console.log("async2");
}
console.log("script start");
setTimeout(function() {
console.log("setTimeout0");
}, 0);
setTimeout(function() {
console.log("setTimeout3");
}, 3);
setImmediate(() => console.log("setImmediate"));
process.nextTick(() => console.log("nextTick"));
async1();
new Promise(function(resolve) {
console.log("promise1");
resolve();
console.log("promise2");
}).then(function() {
console.log("promise3");
});
console.log("script end");
这里的分析和前面讲的一样,这里大家可以看看自己分析分析。
如果这道题可以分析正确的话,我相信大家对nodeJS中的事件循环已经有一个清楚的认知了!小伙伴们可以在评论区踊跃发言!如有不对,请及时纠正!