node.js事件循环 event loop

Nodejs事件循环 (event loop)

node.js 事件循环的概念

当node.js 启动的时候会初始化eventloop ,每一个evnet loop 都会包含如下6个循环阶段,node.js 事件循环和浏览器事件循环完全不一样。

官网文档:https://nodejs.org/zh-cn/docs/guides/event-loop-timers-and-nexttick/

  • timers
  • pending callbacks (I/O callbakcs)
  • idle, prepare
  • poll
    • connections
    • incoming
    • data, etc
  • check
  • close callbacks

阶段概述

  • times(定时器):此阶段执行那些由 setTimeout()setInval() 调度的回调函数。
  • pending callbacks (I/O 回调):此阶段几乎会执行几乎所有的回调函数,除了close callbacks(关闭回调,不常用)和那些由 timerssetImmedieate() 调度的回调。
  • idle, prepare(空转): 此阶段只在内部使用。
  • poll(轮询): 检索新的 I/O 事件,在恰当的时候 Node 会阻塞在这个阶段。(此阶段比较复杂也是nodejs的核心 )
  • check(检查): setImmediate()设置的回调会在此阶段执行。
  • c lose callbacks(关闭事件的回调): 诸如 secket.on(close, ...) 此类的回调在此阶段被调用。

setImmediate是什么?

setImmediate 约等于 setTimeout( callback , 0)

setImmediate(() => {
    console.log(2222)
})

console.log(1111)

// 打印结果
1111
2222

在事件循环的每次运行之间,Node.js 会检查它是否在等待异步 I/O 或定时器,如果没有的话就会自动关闭。

如果event loop 进入 poll 阶段,且代码未设定timer,将会发生下面的情况:

  • 如果 poll queue 不为空, event loop 将同步的执行 queue 里的 callback ,直至 queue 为空,或执行的 callback 到达系统上限。
  • 如果 poll queue 为空,将会发生下面情况:
    • 如果代码已经被 setImmediate() 设定了 callback ,event loop 将结束poll阶段进入 check 阶段,并执行 check 阶段的 queue (check 阶段的 callback 是 setImmediate 设定的)
    • 如果代码没有设定 setImmediate(callback) , event loop 将阻塞在该阶段等待 callbacks 假如poll queue 一旦到达就立即执行。

如果evnet loop 进入 poll 阶段,且代码设定了 timer:

  • 如果 poll queue 进入空状态时(即poll 阶段为空闲状态), event loop 将检查timers,如果有一个或多个timers 时间已经到达,event loop 将按循环顺序进入timers阶段,并执行 timer queue。

## 执行顺序

如果setTimeout 为0,实际执行时间是多少?

在nodejs中,如果setTimeout 为 0, 那么它的执行时间最少是1ms

在浏览器中,如果setTimeout 为0, 那么它的执行时间最少是 4ms

为什么 setImmediate() 与 setTimeout() 一起执行时,他们的执行顺序不确定?

setImmediate(()=>{
    console.log(111)
})

setTimeout(()=>{
    console.log(222)
},0)

// 111 222
// 222 111
两种情况都有

这是因为event loop 启动需要时间,而在这个启动时间具有不确定性,导致了代码执行顺序的差异。他们的执行顺序取决于event loop 的启动速度。

如果 event loop 启动后从 timer到 I/O 再到 poll 阶段 如果用了 1.5ms ,那么 timeout 的时间已经到了这是settimeout就会先执行。这时打印顺序是 222 111

如果event loop 启动后从 timer到 I/O 再到 poll 阶段 如果用了 0.8ms ,这时timeout实际执行是1ms,这时poll就会跳过timeout 进入 check 阶段执行 setImmediate。而check执行完,进入第二轮时间循环后就会执行timeout了。这时打印顺序是 111 222

process.nextTick()

process.nextTick() 不在event loop 的任何阶段,而是各个阶段切换的中间执行,即从一个阶段切换到下一个阶段前,穿插着执行。

设计原因

允许开发者通过递归调用process.nextTick() 来阻塞 I/O 操作。

应用场景

  1. 在多个事件里交叉执行CPU运算密集型任务:
const http = require('http')

function compute(){
    process.nextTick(compute)
}

http.createServer((req, res) => {
    // http服务请求的时候,还能抽空进行一些计算
    res.writeHead(200, {
        'Content-Type': 'text/plain'
    })
    res.end('hello word')
}).listen(3000, '127.0.0.1')

compute()

在这种模式下不需要递归调用compute(),只需要在事件循环中使用process.nextTick() 定义compute() 在下一个时间点执行即可。在这个过程中,如果新的http请求进来,时间循环机制会先处理新的请求,然后再调用compute , 反之,如果直接把compute()放在递归调用里,系统会一直阻塞在compute() 里,无法处理新的http请求。

  1. 保持回调函数异步执行的原则

    当给一个函数定义回调时,要确保这个回调函数是被异步执行的。而下面这个例子就违反了这一原则。

   function asyncFake(data, callback) {
       if(data === 'foo') {
           callback(true)
       }
       else{
         callback(false)
       }
   }

   asyncFake('bar', result => {
       // this callback is actually called asynchronously!
   })

在node.js 中有这样一段代码

const client = net.connect(8000, () => {
  client.write('hello wrod')
})

上面代码因为某种原因, net.connect() 变成了同步执行,回调函数就会被立刻执行,因此回调函数写到客户端的变量就永远不会初始化了。在这种情况下就可以使用process.nextTick()把回调函数编程异步执行。

const client = net.connect(8000, () => {
  process.nextTick(()=> {
    clllback(data === 'foo')
  })
})
  1. 用在事件触发过程中

EventEmitter 有2个比较核心的方法,on和emit,node自带发布/订阅模式

// nodejs 的发布订阅
const EventEmitter = require('events').EventEmitter

class App extends EventEmitter{

}

var app = new App()

// on 订阅
app.on('start', () => {
    console.log('start')
})

// emit 触发 是个同步的方法
app.emit('start')

console.log('111')

process.nextTick() 与 setTimeout 和 setImmediate 的执行顺序

process.nextTick()的执行时间是不确定的,如果在执行过程中会穿插在每个阶段。

const fs = require('fs')
const path = require('path')

fs.readFile(path.resolve(__dirname, "/1.js"),  ()=> {

    setTimeout(() => {
        console.log('1.timeout')
    },0)

    setImmediate(() => {
        console.log('2.immediate')
    })

    process.nextTick(() => {
        console.log('3.nextTick')
    })

    process.nextTick(() => {
        console.log('4.nextTick')
    })
})

// 打印结果
3.nextTick
4.nextTick
2.immediate
1.timeout

原文地址:https://www.cnblogs.com/liea/p/12399051.html

时间: 2024-10-12 16:27:36

node.js事件循环 event loop的相关文章

Node.js 事件循环

原文:https://github.com/nodejs/node/blob/master/doc/topics/event-loop-timers-and-nexttick.md 什么是事件循环(Event Loop) 事件循环能让 Node.js 执行非阻塞 I/O 操作 -- 尽管JavaScript事实上是单线程的 -- 通过在可能的情况下把操作交给操作系统内核来实现. 由于大多数现代系统内核是多线程的,内核可以处理后台执行的多个操作.当其中一个操作完成的时候,内核告诉 Node.js,

JavaScript:彻底理解同步、异步和事件循环(Event Loop) (转)

原文出处:https://segmentfault.com/a/1190000004322358 一. 单线程 我们常说"JavaScript是单线程的". 所谓单线程,是指在JS引擎中负责解释和执行JavaScript代码的线程只有一个.不妨叫它主线程. 但是实际上还存在其他的线程.例如:处理AJAX请求的线程.处理DOM事件的线程.定时器线程.读写文件的线程(例如在Node.js中)等等.这些线程可能存在于JS引擎之内,也可能存在于JS引擎之外,在此我们不做区分.不妨叫它们工作线程

[Node.js] Use nodejs-dashboard event loop delay with hrtime()

In this lesson, you will learn how to use the Formidable nodejs-dashboard event loop delay to identify expensive operations in your code. An example application with excessive synchronous file system write operations is used as well as the provided j

JavaScript:彻底理解同步、异步和事件循环(Event Loop)

转自:https://segmentfault.com/a/1190000004322358 http://www.cnblogs.com/rubylouvre/ http://www.tuicool.com/articles/7B7Bju http://soyoung.blog.51cto.com/7578959/1550874/ http://bbs.csdn.net/topics/390765530 https://cnodejs.org/topic/52414b3a101e5745219

node.js事件轮询(1)

事件轮询(引用) 事件轮询是node的核心内容.一个系统(或者说一个程序)中必须至少包含一个大的循环结构(我称之为"泵"),它是维持系统持续运行的前提.nodejs中一样包含这样的结构,我们叫它"事件轮询",它存在于主线程中,负责不停地调用开发者编写的代码.我们可以查看nodejs官方网站上对nodejs的说明: Node is similar in design to and influenced by systems like Ruby's Event Mach

node.js 事件发射器模式

目录: 前言 Node.js事件驱动介绍 Node.js事件 注册并发射自定义Node.js事件 EventEmitter介绍 EventEmitter常用的API error事件 继承EventEmitter 前言: 今天事儿太多了,没有发太多的东西.不好意思了各位. 本篇主要介绍Node.js中的事件驱动,至于Node.js事件概念的东西,太多了. 本系列课程主要抱着的理念就是,让大家慢慢的入门,我也尽量写的简单一点. 所以呢,本文事件驱动,大家的目标应该是:理解Node.js的事件驱动.会

Node.js 事件

Node.js 事件 Node.js 所有的异步I/O 操作在完成时都会发送一个事件到事件队列. Node.js里面的许多对象都会分发事件:一个net.Server对象会在每次有新连接时分发一个事件, 一个fs.readStream对象会在文件被打开的时候发出一个事件. 所有这些产生事件的对象都是 events.EventEmitter 的实例. 你可以通过require("events");来访问该模块. 下面我们用一个简单的例子说明 EventEmitter 的用法: //even

node.js零基础详细教程(4):node.js事件机制、node异步IO操作

第四章 建议学习时间3小时  课程共10章 学习方式:详细阅读,并手动实现相关代码 学习目标:此教程将教会大家 安装Node.搭建服务器.express.mysql.mongodb.编写后台业务逻辑.编写接口,最后完成一个完整的项目后台,预计共10天课程. node.js事件机制 node.js是单线程,但是通过事件和回调支持并发,可以实现非常高的性能. node.js所有的API都是通过异步调用.第一堂课的时候,我们写过一个同步和异步的示例(如下),当初说到:同步代码先执行完成,然后才执行异步

js事件循环机制辨析

?对于新接触js语言的人来说,最令人困惑的大概就是事件循环机制了.最开始这也困惑了我好久,花了我几个月时间通过书本,打代码,查阅资料不停地渐进地理解他.接下来我想要和大家分享一下,虽然可能有些许错误的地方,希望大家不吝赐教,感谢感谢. ?这是所涉及的知识点: 观察者模式 js的事件循环机制 js事件循环机制优缺点及与多线程的比较 观察者模式 ?js的事件循环机制是基于观察者模式的,而跟观察者模式相对应的是轮询,我们先来说说轮询的原理. ?我们将轮询映射在现实世界中即为:B不停到A的房间观察房间里