跳至主要內容

1.2 Node.js 事件循环 🎉

刘春龙...大约 2 分钟NODEJSNODE后端nodejs

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