程序员scholar 程序员scholar
首页
  • Java 基础

    • JavaSE
    • JavaIO
    • JavaAPI速查
  • Java 高级

    • JUC
    • JVM
    • Java新特性
    • 设计模式
  • Web 开发

    • Servlet
    • Java网络编程
  • Web 标准

    • HTML
    • CSS
    • JavaScript
  • 前端框架

    • Vue2
    • Vue3
    • Vue3 + TS
    • 微信小程序
    • uni-app
  • 工具与库

    • jQuery
    • Ajax
    • Axios
    • Webpack
    • Vuex
    • WebSocket
    • 第三方登录
  • 后端与语言扩展

    • ES6
    • Typescript
    • node.js
  • Element-UI
  • Apache ECharts
  • 数据结构
  • HTTP协议
  • HTTPS协议
  • 计算机网络
  • Linux常用命令
  • Windows常用命令
  • SQL数据库

    • MySQL
    • MySQL速查
  • NoSQL数据库

    • Redis
    • ElasticSearch
  • 数据库

    • MyBatis
    • MyBatis-Plus
  • 消息中间件

    • RabbitMQ
  • 服务器

    • Nginx
  • Spring框架

    • Spring6
    • SpringMVC
    • SpringBoot
    • SpringSecurity
  • SpringCould微服务

    • SpringCloud基础
    • 微服务之DDD架构思想
  • 日常必备

    • 开发常用工具包
    • Hutoll工具包
    • IDEA常用配置
    • 开发笔记
    • 日常记录
    • 项目部署
    • 网站导航
    • 产品学习
    • 英语学习
  • 代码管理

    • Maven
    • Git教程
    • Git小乌龟教程
  • 运维工具

    • Docker
    • Jenkins
    • Kubernetes
  • 算法笔记

    • 算法思想
    • 刷题笔记
  • 面试问题常见

    • 十大经典排序算法
    • 面试常见问题集锦
关于
GitHub (opens new window)
首页
  • Java 基础

    • JavaSE
    • JavaIO
    • JavaAPI速查
  • Java 高级

    • JUC
    • JVM
    • Java新特性
    • 设计模式
  • Web 开发

    • Servlet
    • Java网络编程
  • Web 标准

    • HTML
    • CSS
    • JavaScript
  • 前端框架

    • Vue2
    • Vue3
    • Vue3 + TS
    • 微信小程序
    • uni-app
  • 工具与库

    • jQuery
    • Ajax
    • Axios
    • Webpack
    • Vuex
    • WebSocket
    • 第三方登录
  • 后端与语言扩展

    • ES6
    • Typescript
    • node.js
  • Element-UI
  • Apache ECharts
  • 数据结构
  • HTTP协议
  • HTTPS协议
  • 计算机网络
  • Linux常用命令
  • Windows常用命令
  • SQL数据库

    • MySQL
    • MySQL速查
  • NoSQL数据库

    • Redis
    • ElasticSearch
  • 数据库

    • MyBatis
    • MyBatis-Plus
  • 消息中间件

    • RabbitMQ
  • 服务器

    • Nginx
  • Spring框架

    • Spring6
    • SpringMVC
    • SpringBoot
    • SpringSecurity
  • SpringCould微服务

    • SpringCloud基础
    • 微服务之DDD架构思想
  • 日常必备

    • 开发常用工具包
    • Hutoll工具包
    • IDEA常用配置
    • 开发笔记
    • 日常记录
    • 项目部署
    • 网站导航
    • 产品学习
    • 英语学习
  • 代码管理

    • Maven
    • Git教程
    • Git小乌龟教程
  • 运维工具

    • Docker
    • Jenkins
    • Kubernetes
  • 算法笔记

    • 算法思想
    • 刷题笔记
  • 面试问题常见

    • 十大经典排序算法
    • 面试常见问题集锦
关于
GitHub (opens new window)
npm

(进入注册为作者充电)

  • node.js

    • Node.js 初识
      • 1. Node.js 诞生史
      • 2. Node.js 是什么
      • 3. Node.js 的特点
        • 3.1 优点
        • 3.2 不足之处
      • 4. Node.js 的应用场景
      • 5. Node 中函数的特点
        • 5.1 外层函数的作用
        • 5.2 输出外层函数
        • 5.3 直接使用外层函数参数
        • 5.4 外层函数的作用
      • 6. Node 中的 global
        • 6.1 Node 的组成
        • 6.2 global 的一些常用属性
        • 6.3 示例代码
        • 6.4 global 与 this 的区别
      • 7. Node.js 中的事件循环模型
        • 概览
        • 阶段描述
        • 1. Timers 阶段
        • 2. Pending Callbacks 阶段
        • 3. Idle, Prepare 阶段
        • 4. Poll 阶段
        • 5. Check 阶段
        • 6. Close Callbacks 阶段
        • setTimeout vs setImmediate
    • 包与 npm 包管理器
    • Buffer 缓冲器
    • fs 文件系统
    • MongoDB 非关系型数据库
  • node.js
  • node.js
scholar
2024-08-02
目录

Node.js 初识

# Node.js 初识

# 1. Node.js 诞生史

Node.js之父:Ryan Dahl(瑞安·达尔)

  • 并非科班出身的开发者,在2004年在纽约的罗彻斯特大学数学系读博士。
  • 2006年退学,来到智利的Valparaiso小镇。
  • 期间曾熬夜做了一些不切实际的研究,例如如何通过云进行通信。
  • 偶然的机会,走上了编程之路,生活方式变为接项目,然后去客户的地方工作。
  • 工作中遇到了主流服务器的瓶颈问题,尝试着自己去解决,费尽周折没有办法。
  • 2008年Google公司Chrome V8引擎横空出世,JavaScript脚本语言的执行效率得到质的提升,他的想法与Chrome V8引擎碰撞出激烈的火花。
  • 2009年的2月,按新的想法他提交了项目的第一行代码,这个项目的名字最终被定名为 “node”。
  • 2009年5月,正式向外界宣布他做的这个项目。
  • 2009年底,Ryan Dahl在柏林举行的JSConf EU会议上发表关于Node.js的演讲,之后Node.js逐渐流行于世。
  • Ryan Dahl于2010年加入Joyent公司,全职负责Node.js项目的开发。此时Node.js项目已经从个人项目变成一个公司组织下的项目。

# 2. Node.js 是什么

Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境。

node1

传统的 JavaScript 只能运行在浏览器端,脱离浏览器不能运行,也就不能操作本地的文件或者创建文件,也不能进行网络编程,Node.js 的出现打破了这一局面。
Node.js 编写的代码还是 JS,所以开发者需要利用 Chrome V8 来运行 JS。Node.js 借助了 C/C++ 中的 libuv 库来实现文件读取和事件循环。我们不必深挖其中的原理,只需要知道如何使用就行,Node.js 已经为我们打包好了相关接口。

# 3. Node.js 的特点

# 3.1 优点

  • 异步非阻塞的 I/O(I/O线程池)
  • 特别适用于 I/O 密集型应用(对比传统 Java 服务器)
  • 事件循环机制
  • 单线程(成也单线程,败也单线程)
  • 跨平台

解释:

  • 异步非阻塞的 I/O(I/O线程池)
    I/O是指inout/ouput,这里是指文件的读写,数据库的操作等等。同步会造成阻塞问题,按照顺序来进行操作。异步是指做这一件事的时候可以做其他事情,非阻塞。
    I/O线程池:让一个线程随时随地处于待命状态,以便下次更加快速的执行任务。
  • 特别适用于 I/O 密集型应用
    某个项目需要频繁进行I/O操作,就成为I/O 密集型应用。
  • 事件循环机制
    Node.js 脱离了浏览器,浏览器有事件循环机制,但 Node.js 也提供了自己的独有的一个事件循环机制。
  • 单线程(成也单线程,败也单线程)
    单线程要想实现异步,就必须要有自己的 “事件循环模型”。
  • 跨平台
    • JS 跨平台:js——js引擎——由谷歌等设计
    • java跨平台:java——jvm虚拟机
    • Node.js 也跨平台

# 3.2 不足之处

  • 回调函数嵌套太多、太深(俗称回调地狱)
  • 单线程,处理不好CPU 密集型任务

CPU密集型与IO密集型:

  • CPU 密集型:需要过多判断,要做的事情不明确
  • IO 密集型:事情明确

简单 web 交互模型:
node3

Node.js 和 Java 服务器对比:

  • Java 服务器可以有多 “服务员” ,增加服务器空间来实现高并发,成本也高,适用于大企业。
  • Node.js 的服务器只有一个 “服务员”,每次收到任务请求的时候,一对一的服务,向数据库请求数据,通过回调函数实现高并发。适用于 I/O 密集型应用,适用于 个人或中小型企业或者微信小程序搭建服务器。
  • 所以对于 CPU 密集型应用,会频繁 “点餐”,这时候一对一的 “Node” 就废掉了。

# 4. Node.js 的应用场景

  • Web服务API,比如RESTful API(本身没有太多的逻辑,只需要请求API,组织数据进行返回即可)
  • 服务器渲染页面,提升速度
  • 后端的 Web 服务,例如跨域、服务器端的请求

# 5. Node 中函数的特点

在 Node.js 中,每个模块(即每个 JavaScript 文件)都会被一个外层函数包裹。这种机制为模块化开发提供了支持,并且在某种程度上保护了变量的作用域。

# 5.1 外层函数的作用

Node.js 在每个模块的外层包裹了一个函数,该函数有如下结构:

function (exports, require, module, __filename, __dirname) {
    // 模块代码
}
1
2
3
  • exports:用于支持 CommonJS 模块化的导出语法。
  • require:用于支持 CommonJS 模块化的引入语法。
  • module:用于支持 CommonJS 模块化的导出语法。
  • __filename:当前运行文件的绝对路径。
  • __dirname:当前运行文件所在文件夹的绝对路径。

# 5.2 输出外层函数

可以通过 arguments.callee 来获取函数自身,从而输出外层函数的内容:

function demo() {
    // 输出函数本身
    console.log(arguments.callee.toString());
}

demo();
1
2
3
4
5
6

在 Node.js 中执行以下代码,可以获取到外层函数的结构:

console.log(arguments.callee.toString());
1

执行后,得到类似以下结构的外层函数:

function (exports, require, module, __filename, __dirname) {
    // 模块内容
}
1
2
3

# 5.3 直接使用外层函数参数

Node.js 允许我们在模块内部直接使用这些参数:

console.log(__filename); // 输出当前文件的绝对路径
console.log(__dirname);  // 输出当前文件所在目录的绝对路径
1
2

# 5.4 外层函数的作用

  • 模块化支持:提供模块化语法的支持,使得每个文件可以独立成为一个模块。
  • 作用域保护:隐藏模块内部的实现细节,避免变量污染全局作用域,从而提高安全性。

# 6. Node 中的 global

在 Node.js 中,全局对象 global 类似于浏览器中的 window 对象,但在服务器端环境中有其独特之处。

# 6.1 Node 的组成

在浏览器中,JavaScript 由以下三部分组成:

  • BOM(Browser Object Model):浏览器对象模型,提供与浏览器交互的能力。
  • DOM(Document Object Model):文档对象模型,允许对 HTML 和 XML 文档进行动态访问和操作。
  • ECMAScript:JavaScript 语言的核心规范。

在 Node.js 中,JavaScript 环境有以下特点:

  • 没有 BOM:因为服务器不需要与浏览器交互。
  • 没有 DOM:因为没有浏览器窗口,也就没有文档对象模型。
  • 支持 ECMAScript:几乎包含了所有的 ECMAScript 规范。
  • 没有 window 对象:Node.js 使用 global 作为全局对象。

# 6.2 global 的一些常用属性

Node.js 提供了 global 对象,用于全局变量和函数的访问。以下是一些常用的全局属性和方法:

console.log(global);
1
  • setInterval:设置循环定时器。
  • clearInterval:清空循环定时器。
  • setTimeout:设置延迟定时器。
  • clearTimeout:清空延迟定时器。
  • setImmediate:设置立即执行函数。
  • clearImmediate:清空立即执行函数。

# 6.3 示例代码

以下是一个简单的示例,演示如何使用 global 对象设置和清除定时器:

// 设置一个每秒执行一次的循环定时器
const intervalId = setInterval(() => {
    console.log('每秒执行一次');
}, 1000);

// 3秒后清除定时器
setTimeout(() => {
    clearInterval(intervalId);
    console.log('定时器已清除');
}, 3000);

// 设置一个立即执行函数
const immediateId = setImmediate(() => {
    console.log('立即执行函数');
});

// 清除立即执行函数
clearImmediate(immediateId); // 由于被清除,这行不会输出任何内容
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# 6.4 global 与 this 的区别

  • 在 Node.js 中,global 是全局对象,类似于浏览器中的 window,但更适合服务器端应用。
  • 在模块中,this 默认指向模块的 exports 对象,而不是 global,这与浏览器中的行为不同。
console.log(this); // 输出:{}
console.log(global === this); // 输出:false
1
2

总结

Node.js 的模块化特性和全局对象 global 为服务器端开发提供了强大的工具。通过外层函数的封装,Node.js 实现了模块化开发和作用域隔离,提高了代码的安全性和可维护性。同时,global 对象提供了一些常用的全局方法,使得开发者可以方便地处理定时任务和立即执行函数。了解这些特性和工具,可以帮助开发者更好地利用 Node.js 开发高效的服务器端应用。

Node.js 的事件循环模型是其高性能和非阻塞 I/O 的核心机制。它通过事件驱动架构,在单线程中高效处理多任务。以下是 Node.js 事件循环模型的详细介绍。

# 7. Node.js 中的事件循环模型

# 概览

Node.js 的事件循环模型分为 6 个阶段,每个阶段处理特定类型的事件和回调函数。这些阶段按顺序循环执行。

   ┌───────────────────────────┐
┌─>│           timers          │
│  └─────────────┬─────────────┘
│  ┌─────────────┴─────────────┐
│  │     pending callbacks     │
│  └─────────────┬─────────────┘
│  ┌─────────────┴─────────────┐
│  │       idle, prepare       │
│  └─────────────┬─────────────┘      ┌───────────────┐
│  ┌─────────────┴─────────────┐      │   incoming:   │
│  │           poll            │<─────┤  connections, │
│  └─────────────┬─────────────┘      │   data, etc.  │
│  ┌─────────────┴─────────────┐      └───────────────┘
│  │           check           │
│  └─────────────┬─────────────┘
│  ┌─────────────┴─────────────┐
└──┤      close callbacks      │
   └───────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# 阶段描述

# 1. Timers 阶段

  • 功能:执行 setTimeout 和 setInterval 设定的回调。
  • 过程:
    1. 计时器开始计时。
    2. 到达计时器设定的时间后,执行相应的回调函数。

# 2. Pending Callbacks 阶段

  • 功能:处理一些系统操作(如 TCP 错误类型)的回调。
  • 说明:此阶段主要用于处理延迟执行的 I/O 回调。

# 3. Idle, Prepare 阶段

  • 功能:仅用于内部系统的准备工作。
  • 说明:在事件循环的这部分通常不需要开发者关注。

# 4. Poll 阶段

  • 功能:获取新的 I/O 事件,执行与 I/O 相关的回调。
  • 过程:
    • 如果有回调需要执行:
      • 从回调队列中取出回调函数,依次执行,直到队列为空或达到系统限制。
    • 如果回调队列为空:
      • 如果存在 setImmediate 回调,则进入 Check 阶段。
      • 如果没有 setImmediate 回调,则在此阶段等待新的 I/O 事件。
      • 如果定时器到点,则进入 Check 阶段。

# 5. Check 阶段

  • 功能:执行 setImmediate 设定的回调。
  • 说明:专门用于处理 setImmediate 的回调。

# 6. Close Callbacks 阶段

  • 功能:执行一些关闭事件的回调,如 socket.on('close', ...)。
  • 说明:在此阶段处理关闭事件的回调。

特殊情况

  • process.nextTick():
    • 用于在当前操作完成后立即执行的回调。
    • 优先级比事件循环的其他阶段高,被称为“VIP 回调”。

# setTimeout vs setImmediate

  • 当没有主线程阻塞时,setTimeout 和 setImmediate 的执行顺序不确定,取决于事件循环的执行时间。
  • 当存在主线程任务时,setTimeout 会优先于 setImmediate 执行。

以下是一个示例,展示了不同回调的执行顺序:

// 延迟执行函数
setTimeout(() => {
    console.log('setTimeout 指定的回调');
});

// 立即执行函数(回调)
setImmediate(() => {
    console.log('我是 setImmediate 执行的回调');
});

// 立即执行函数(VIP 回调)
process.nextTick(() => {
    console.log('process.nextTick 指定的回调函数');
});

console.log('我是主线程上的代码');
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

输出结果

我是主线程上的代码
process.nextTick 指定的回调函数
setTimeout 指定的回调
我是 setImmediate 执行的回调
1
2
3
4

解释

  • 主线程代码:首先执行主线程上的代码,输出 "我是主线程上的代码"。
  • process.nextTick():在主线程代码执行完后立即执行,输出 "process.nextTick 指定的回调函数"。
  • setTimeout:由于定时器已经到达,因此在 Timers 阶段执行,输出 "setTimeout 指定的回调"。
  • setImmediate:在 Check 阶段执行,输出 "我是 setImmediate 执行的回调"。

总结

Node.js 的事件循环模型通过多个阶段处理不同类型的事件和回调,确保在单线程环境中高效处理 I/O 操作。通过理解事件循环的工作原理,开发者可以更好地优化代码,提升应用性能。process.nextTick() 和 setImmediate 等工具允许开发者精确控制代码的执行顺序,在实际应用中具有重要意义。

编辑此页 (opens new window)
上次更新: 2024/12/28, 18:32:08
包与 npm 包管理器

包与 npm 包管理器→

Theme by Vdoing | Copyright © 2019-2025 程序员scholar
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式