什么是事件轮询

事件循环是 Node.js 处理非阻塞 I/O 操作的机制——尽管 JavaScript 是单线程处理的——当有可能的时候,它们会把操作转移到系统内核中去。

下面的图表显示了事件循环的概述以及操作顺序。

┌───────────────────────────┐

┌─>│ timers │

│ └─────────────┬─────────────┘

│ ┌─────────────┴─────────────┐

│ │ IO / callbacks │

│ └─────────────┬─────────────┘

│ ┌─────────────┴─────────────┐

│ │ idle, prepare │

│ └─────────────┬─────────────┘ ┌───────────────┐

│ ┌─────────────┴─────────────┐ │ incoming: │

│ │ poll │

│ └─────────────┬─────────────┘ │ data, etc. │

│ ┌─────────────┴─────────────┐ └───────────────┘

│ │ check │

│ └─────────────┬─────────────┘

│ ┌─────────────┴─────────────┐

└──┤ close callbacks │

└───────────────────────────┘

三大关键阶段

timer:执行定时器时,如 setTimeout、setInterval,在 timers 阶段执行

poll:异步操作,比如文件I/O,网络I/O等,通过'data'、 'connect'等事件通知 JS 主线程并执行回调的,此阶段就是 poll 轮询阶段

check:这是一个比较简单的阶段,直接执行 setImmdiate 的回调。

注意,若 2 阶段结束后,当前存在到时间的定时器,那么拿出来执行,eventLoop 将再回到 timer 阶段

阶段流程概述

timers: 本阶段执行已经安排的 setTimeout() 和 setInterval() 的回调函数

IO / callbacks: 执行 I/O 异常的回调,如TCP 连接遇到 ECONNREFUSED

idle, prepare: 仅系统内部使用,只是表达空闲、预备状态(第2阶段结束,poll 未触发之前)

poll: 检索新的 I/O 事件;执行与 I/O 相关的回调(几乎所有情况下,除了关闭的回调函数),node 将在此处阻塞。

check: setImmediate() 回调函数在这里执行.

close callbacks: 一些准备关闭的回调函数,如:socket.on('close', ...)

在每次运行的事件循环之间,Node.js 检查它是否在等待任何异步 I/O 或计时器,如果没有的话,则关闭干净。

timers

timers 指定 可执行所提供回调 的 时间阈值,poll 阶段 控制何时定时器执行。

一旦 poll queue 为空,事件循环将检查 已达到时间阈值的timer计时器。如果一个或多个计时器已准备就绪,则事件循环将回到 timer 阶段以执行这些计时器的回调

pending callbacks

此阶段对某些系统操作(如 TCP 错误类型)执行回调。例如,如果 TCP 套接字在尝试连接时接收到 ECONNREFUSED,则某些 *nix 的系统希望等待报告错误。这将被排队以在 pending callbacks 阶段执行。

poll

轮询 阶段有两个重要的功能:

计算应该阻塞和 poll I/O 的时间。

然后,处理 poll 队列里的事件。

当事件循环进入 poll阶段且 timers scheduled,将发生以下两种情况之一:

if the poll queue is not empty, 事件循环将循环访问其回调队列并同步执行它们,直到队列已用尽,或者达到了与系统相关的硬限制

If the poll queue is empty,还有两件事发生

如果脚本已按 setImmediate() 排定,则事件循环将结束 轮询 阶段,并继续 检查 阶段以执行这些计划脚本。

如果脚本尚未按 setImmediate()排定,则事件循环将等待回调添加到队列中,然后立即执行。

一旦 poll queue 为空,事件循环将检查 已达到时间阈值的timer计时器。如果一个或多个计时器已准备就绪,则事件循环将回到 timer 阶段以执行这些计时器的回调。

check

通常,在执行代码时,事件循环最终会命中轮询阶段,等待传入连接、请求等。但是,如果回调已计划为 setImmediate(),并且轮询阶段变为空闲状态,则它将结束并继续到检查阶段而不是等待轮询事件。

setImmediate() 实际上是一个在事件循环的单独阶段运行的特殊计时器。它使用一个 libuv API 来安排回调在 poll 阶段完成后执行。

close callbacks

如果套接字或处理函数突然关闭(例如 socket.destroy()),则'close' 事件将在这个阶段发出。否则它将通过 process.nextTick() 发出。

setImmediate() 对比 setTimeout()

setImmediate() 和 setTimeout() 很类似,但何时调用行为完全不同。

setImmediate() 设计为在当前 轮询 阶段完成后执行脚本。

setTimeout() 计划在毫秒的最小阈值经过后运行的脚本。

执行计时器的顺序将根据调用它们的上下文而异,如果二者都从主模块内调用,则计时将受进程性能的约束,两个计时器的顺序是非确定性的。

// timeout_vs_immediate.js

setTimeout(() => {

console.log('timeout');

}, 0);

setImmediate(() => {

console.log('immediate');

});

$ node timeout_vs_immediate.js

timeout

immediate

$ node timeout_vs_immediate.js

immediate

timeout

但是,如果你把这两个函数放入一个 I/O 循环内调用,setImmediate 总是被优先调用:

// timeout_vs_immediate.js

const fs = require('fs');

fs.readFile(__filename, () => {

setTimeout(() => {

console.log('timeout');

}, 0);

setImmediate(() => {

console.log('immediate');

});

});

$ node timeout_vs_immediate.js

immediate

timeout

$ node timeout_vs_immediate.js

immediate

timeout

使用 setImmediate() 超过 setTimeout() 的主要优点是 setImmediate() 在任何计时器(如果在 I/O 周期内)都将始终执行,而不依赖于存在多少个计时器。

process.nextTick()

process.nextTick() 在技术上不是事件循环的一部分,无论事件循环的当前阶段如何,都将在当前操作完成后处理 nextTickQueue。这里的一个操作被视作为一个从 C++ 底层处理开始过渡,并且处理需要执行的 JavaScript 代码。

回顾我们的关系图,任何时候在给定的阶段中调用 process.nextTick(),所有传递到 process.nextTick() 的回调将在事件循环继续之前得到解决。这可能会造成一些糟糕的情况, 因为它允许您通过进行递归 process.nextTick() 来“饿死”您的 I/O 调用,阻止事件循环到达 轮询 阶段。

一个题目

// test.js

process.nextTick(function() {

console.log('next tick');

});

setTimeout(function() {

console.log('settimeout');

});

(async function() {

console.log('async promise');

})();

setImmediate(function() {

console.log('setimmediate');

});

$ node test.js

async promise

next tick

settimeout

setimmediate

没有await,async那句其实是同步执行的,故而第一句输出。

next tick 在任何事件循环阶段继续之前得到解决,故而第二句

setTimeout 在主线程中与 setImmediate 的执行顺序是非确定性的

// test.js

setTimeout(function () {

process.nextTick(function() {

console.log('next tick');

});

setTimeout(function() {

console.log('settimeout');

});

(async function() {

console.log('async promise');

})();

setImmediate(function() {

console.log('setimmediate');

});

})

$ node test.js

async promise

next tick

setimmediate

settimeout

setimmediate 与 settimeout 放入一个 I/O 循环内调用,则 setImmediate 总是被优先调用

node >= 11 ?

setTimeout(()=>{

console.log('timer1')

setImmediate(function () { console.log('immd 1'); })

Promise.resolve().then(function() {

console.log('promise1')

})

}, 0)

setTimeout(()=>{

console.log('timer2')

setImmediate(function () { console.log('immd 2'); })

Promise.resolve().then(function() {

console.log('promise2')

})

}, 0)

在 node 11 及以上版本打印得

timer1

promise1

timer2

promise2

immd 1

immd 2

在 node 版本为 8.11.2 打印

timer1

timer2

promise1

promise2

immd 1

immd 2

这是因为 < 11 得版本中

若第一个定时器任务出队并执行完,发现队首的任务仍然是一个定时器,那么就将微任务暂时保存,直接去执行新的定时器任务,当新的定时器任务执行完后,再一一执行中途产生的微任务。

nodejs 和 浏览器关于eventLoop的主要区别

两者最主要的区别在于浏览器中的微任务是在每个相应的宏任务中执行的,而nodejs中的微任务是在不同阶段之间执行的。

node mysql 事件循环_NodeJs 的 Event loop 事件循环机制详解相关推荐

  1. node.js中对Event Loop事件循环的理解

    javascript是单线程的,所以任务的执行都需要排队,任务分为两种,一种是同步任务,一种是异步任务. 同步任务是进入主线程上排队执行的任务,上一个任务执行完了,下一个任务才会执行. 异步任务是不进 ...

  2. 为什么JS是单线程?JS中的Event Loop(事件循环)?JS如何实现异步?setimeout?

    https://segmentfault.com/a/1190000012806637 https://www.jianshu.com/p/93d756db8c81 首先,请牢记2点: (1) JS是 ...

  3. Event Loop 事件循环简介

    1.Event Loop? Event Loop 其实也是在面试中经常会出现的一个题,前端程序员回答不上来是正常的,因为 Event Loop 是 C++ 实现的,实现原理和 JavaScript 没 ...

  4. Event Loop事件循环机制

    转载自:阮一峰博客<JavaScript 运行机制详解:再谈Event Loop> 一.为什么JavaScript是单线程? JavaScript语言的一大特点就是单线程,也就是说,同一个 ...

  5. event loop 事件轮询机制

    1,event loop(事件轮询/事件循环) 首先我们要知道 由于js是单线程的脚本,异步事件要基于回调来实现的 而event loop 就是异步回调的实现原理 1,js的执行顺序 从前到后,一行一 ...

  6. 【朴灵评注】JavaScript 运行机制详解:再谈Event Loop

    PS: 我先旁观下大师们的讨论,得多看书了~ 别人说的:"看了一下不觉得评注对到哪里去,只有吹毛求疵之感. 比如同步异步介绍,本来就无大错:比如node图里面的OS operation,推敲 ...

  7. JavaScript 运行机制详解(理解同步、异步和事件循环)

    1.为什么JavaScript是单线程? JavaScript语言的一大特点就是单线程,也就是说,同一个时间只能做一件事.那么,为什么JavaScript不能有多个线程呢?这样能提高效率啊. Java ...

  8. JavaScript 运行机制详解:再谈Event Loop

    原文地址:http://www.ruanyifeng.com/blog/2014/10/event-loop.html 一年前,我写了一篇<什么是 Event Loop?>,谈了我对Eve ...

  9. 我看朴灵评注阮一峰的《JavaScript 运行机制详解:再谈Event Loop》

    阮一峰和朴灵对我来说都是大牛,他们俩的书我都买过,阮老师的译作<软件随想录>和朴灵的<深入浅出node.js>.这个事情已经过了4个月了,所以我拿来讲应该也没啥问题. 这件事情 ...

最新文章

  1. 深圳网络推广总结影响网站优化的不友好设计有哪些?
  2. HP-UX平台安装Oracle11gR2数据库
  3. 5000字 大数据时代读书笔记_大数据时代 读书笔记
  4. ansible基本模块-shell
  5. 1-Tenor AF AFT400-实战-基本配置
  6. Java Web实训项目:西蒙购物网(2016)
  7. 华为管理学案例分析_管理学论文5000字如何高质量写作
  8. Springboot 配置类( @Configuration) 不能使用@Value注解从application.propertyes中加载值以及Environment为null解决方案
  9. java List转Map的三种方法(java8语法特性使用)
  10. Missing artifact com.sun tools.jar 1.5.0 system 解决方法
  11. Opencv笔记(四)——绘图函数
  12. 学生计算机 在线使用,中学生计算机基础教程
  13. 最新2019年dnf辅助制作视频教程
  14. Java中成员变量的超详解
  15. 狗和猫有相同的情绪反应吗?
  16. 最短路径--狄克斯特拉(Dijkstra)算法
  17. OA项目之项目简介会议发布
  18. Crash Error Debugging
  19. E. Pencils and Boxes(尺取dp)
  20. 领峰:如何通过今日黄金市场价格制定交易策略

热门文章

  1. jQuery 常用方法总结
  2. C语言嵌入式系统编程修炼之道——屏幕操作篇
  3. WebDriver中的元素状态检测表
  4. Mysql数据库大表归档操作
  5. SQL Server DATEDIFF() 函数(SQL计算时间差)
  6. php class类的用法详细总结
  7. .NET获取不到js写的cookie解决方法
  8. 实体类(VO,DO,DTO)的划分
  9. ThinkPHP5跨控制器调用
  10. elk收集nginx日志