一、为什么要有事件循环?
因为nodejs是运行在单线程环境中的,事情只能一件一件执行,执行完一件事才会执行下一件事。
如果某一事件非常耗时,那么就会阻塞后续事件的执行,导致程序运行中断。
为此,nodejs通过引入了事件循环的方式来调度不同事件的执行。
二、事件循环
node的任务可分为nextTick、微任务、Timer队列的任务、Poll队列的任务、Check队列的任务五类
process.nextTick:
一个tick就是指一个事件周期。而process.nextTick是指在下一个事件循环tick开始之前,调用这个函数。所以nextTick一定要比消息队列的setTimeout要快。
process.nextTick(() => {
console.log('i am the next tick');
})
微任务:一般就是promise
Timer队列的任务:
setTimeout:
setTimeout(() => {
console.log('一秒后执行我')
}, 1000);
// 这是等1秒后把回调函数从异步模块放入Timer队列,而不是立即执行,需要等到调用栈空了在放入调用栈执行,所以,setTimeout并不是严格的一秒后立即执行
setInterval:
// 如果想要定时执行某些回调函数,则需要用到setInterval。
setInterval(() => {
console.log('每隔2秒执行一次');
}, 2000)
// 要清除上面的定时任务,可以使用clearInterval:
const id = setInterval(() => {
console.log('每隔2秒执行一次');
}, 2000)
clearInterval(id)
// 注意,setInterval是每隔n毫秒启动一个函数,不管该函数是否执行完毕。
// 如果一个函数执行时间太长,就会导致下一个函数同时执行的情况,怎么解决这个问题呢?
// 我们可以考虑在回调函数内部再次调用setTimeout,这样形成递归的setTimeout调用:
const myFunction = () => {
console.log('做完后,隔2s再次执行!');
setTimeout(myFunction, 2000)
}
setTimeout(myFunction, 2000)
Check队列的任务
setImmediate:
setImmediate(() => { console.log('I am immediate!'); })
执行顺序
任务的执行顺序是这样的:
首先
把所有遇到的同步队列都放到调用栈
把nextTick放入nextTick队列
把微任务放入微任务队列
把Timer、Poll、Check这三个队列的任务先放入异步模块中,等时间到了再放到相应的队列中
然后
执行调用栈里面的同步任务
再把nextTick放入调用栈执行
再把微任务放入调用栈执行
最后按照 Timer队列的任务 -> Poll队列的任务 -> Check队列的任务,这一顺序循环把事件循环的任务放入调用栈执行