1.2 Node.js 事件循环 🎉
...大约 2 分钟
1.2 Node.js 事件循环 🎉
Node.js
的事件循环阐明了Node.js
如何做到异步且具有非阻塞的 I/O
。
调用堆栈 💎
调用堆栈是一个 LIFO 队列(后进先出)。
事件循环不断地检查调用堆栈,以查看是否需要运行任何函数。
const fo1 = () => console.log('fo1')
const fo2 = () => console.log('fo2')
const fo3 = () => {
console.log('fo3')
fo1()
fo2()
}
fo3()
// 运行效果: fo3 fo1 fo2
消息队列 💎
const fo1 = () => console.log('fo1')
const fo2 = () => console.log('fo2')
const fo3 = () => {
console.log('fo3')
setTimeout(fo1, 0)
fo2()
}
fo3()
// 运行效果: fo3 fo2 fo1
当调用 setTimeout()
时, Node.js 会启动定时器。 当定时器到期时,则回调函数会被放入消息队列中。包括其它的一些异步的操作,网络请求或者一些事件的订阅,得到响应后它们的回调函数也会被放入消息队列
。
事件循环会赋予调用堆栈优先级,它首先处理在调用堆栈中找到的所有东西,一旦其中没有任何东西,便开始处理消息队列中的东西。
因此,Node.js虽是单线程,但做到了异步且非阻塞
ES6 作业队列 💎
ECMAScript 2015 引入了作业队列的概念,Promise 使用了该队列。 这种方式会尽快地执行异步函数的结果,而不是放在调用堆栈的末尾。
作业队列的优先级高于消息队列。
const fo1 = () => console.log('fo1')
const fo2 = () => console.log('fo2')
const fo3 = () => {
console.log('fo3')
setTimeout(fo1, 0)
new Promise((resolve, reject) => resolve('应该在 fo2 之后、fo1 之前')).then(resolve => console.log(resolve))
fo2()
}
fo3()
// 运行效果: fo3 fo2 应该在fo2之后、fo1之前 fo1
Node.js process.nextTick() 💎
当将一个函数传给 process.nextTick(callFn)
时,则指示引擎在当前操作结束(在下一个事件循环开始之前)时调用此函数:
传递给process.nextTick(callFn)
的函数会在当前的这次事件循环末尾被立即执行。
const fo1 = () => console.log('fo1')
const fo2 = () => console.log('fo2')
const fo3 = () => {
console.log('fo3')
new Promise((resolve, reject) => resolve('应该在 fo1 之后')).then(resolve => console.log(resolve))
process.nextTick(fo1)
fo2()
}
fo3()
// 运行效果: fo3 fo2 fo1 应该在fo1之后
提示
当调用fo3
函数时,首先打印fo3,完事之后继续往下执行,发现是个Promise
,放到作业队列中,继续往下执行,又遇到了process.nextTick(fo1)
,意思是执行完fo3
函数时,先不要去看作业队列,执行完毕fo3
函数时要立刻执行一下fo1
函数,之后再去执行作业队列和消息队列。
Powered by Waline v2.15.7