NodeJs 学习笔记
Node Version: 20.11.1
Node.js 是一个基于 Chrome V8 JavaScript 引擎的开源运行环境,用于开发服务器端和网络应用。Node.js 允许开发者使用 JavaScript 编写命令行工具和服务器端的应用程序,并且可以无缝地在从服务器到桌面应用再到移动设备的各种环境中运行。
1、Node.js 的核心原理包括:
-
事件驱动:
- Node.js 采用事件驱动模型,这意味着它在处理 I/O 操作时不会阻塞线程等待响应。相反,它会异步地执行这些操作,并通过回调函数或者事件通知结果。
-
单线程与多线程:
- Node.js 在主线程中执行 JavaScript 代码,这个线程负责处理所有非阻塞 I/O 请求。对于计算密集型任务或阻塞 I/O 操作,Node.js 可以创建额外的线程(例如 Worker 线程)来处理。
-
非阻塞 I/O:
- Node.js 设计为支持非阻塞 I/O 操作,这意味着 I/O 请求(如文件读写、网络通信等)可以在不等待响应的情况下发出。当操作完成时,Node.js 会通过回调函数通知结果,从而避免了长时间阻塞主线程。
-
事件循环:
- Node.js 的事件循环是其架构的核心部分。它负责监听已完成的 I/O 操作,并将控制权交还给相应的回调函数。事件循环管理着调用栈和异步回调队列。
-
异步 API:
- Node.js 提供了一组异步 API,允许开发者编写非阻塞代码。这些 API 包括文件系统操作、网络请求等。
-
模块化:
- Node.js 支持模块化编程,允许开发者组织和重用代码。Node.js 自带了许多内置模块,同时社区也提供了大量的第三方模块。
2、Node.js 的执行流程简述:
-
加载和编译:
- 当启动 Node.js 应用程序时,它会加载并编译 JavaScript 文件到可执行的字节码。
-
初始化事件循环:
- 初始化事件循环,准备处理事件和回调。
-
执行应用程序代码:
- 执行应用程序的主入口点,通常是
index.js
或app.js
这样的文件。
- 执行应用程序的主入口点,通常是
-
处理 I/O 操作:
- 对于 I/O 请求,Node.js 会将其传递给操作系统进行处理,并将回调添加到事件队列中。
-
事件循环处理:
- 事件循环检查是否有任何已完成的操作需要处理,并调用相应的回调函数。
-
结束:
- 当没有更多的事件或回调要处理时,事件循环结束,应用程序退出。
Node.js 的这种设计使得它非常适合构建高性能、可伸缩的网络应用程序和服务。
3、非阻塞 I/O
非阻塞 I/O(Non-blocking I/O)是一种 I/O 模型,在这种模型下,应用程序在发起 I/O 操作(如文件读写、网络通信等)时不需要等待操作完成即可继续执行其他任务。与之相对的是阻塞 I/O(Blocking I/O),在这种模式下,应用程序必须等待 I/O 操作完成才能继续执行下一步。
1. 非阻塞 I/O 的工作原理:
-
异步执行:
- 当应用程序发起一个 I/O 请求时,它不需要等待 I/O 操作完成就能继续执行后续代码。这样可以确保主线程不会被长时间阻塞。
-
事件驱动:
- 使用事件驱动模型,当 I/O 操作完成时,会触发一个事件,通知应用程序操作已经完成。通常这个通知会通过回调函数、Promise 或者其他异步机制来实现。
-
事件循环:
- 在 Node.js 中,事件循环负责监听已完成的 I/O 操作,并调度相关的回调函数。事件循环是 Node.js 的核心组件之一。
-
I/O 多路复用:
- 为了提高效率,Node.js 使用了 I/O 多路复用技术,例如
epoll
(Linux)和kqueue
(FreeBSD)。这些技术允许操作系统同时监控多个文件描述符的 I/O 活动,并在有活动发生时通知应用程序。
- 为了提高效率,Node.js 使用了 I/O 多路复用技术,例如
-
异步 API:
- Node.js 提供了一系列异步 API,允许开发者编写非阻塞的代码。例如,
fs.readFile()
和net.createConnection()
等方法都是异步的。
- Node.js 提供了一系列异步 API,允许开发者编写非阻塞的代码。例如,
2. 例子说明:
假设你正在编写一个简单的 HTTP 服务器,它需要从磁盘读取文件并发送给客户端。在阻塞 I/O 模型下,服务器会在读取文件时阻塞,直到文件读取完毕才能处理下一个请求。而在非阻塞 I/O 模型下,服务器可以立即返回并开始处理下一个请求,当文件读取完成后,再通过回调函数发送文件内容给客户端。
3. 代码示例(Node.js):
const http = require('http');
const fs = require('fs');
http.createServer((req, res) => {
fs.readFile('./example.txt', (err, data) => {
if (err) {
res.writeHead(500);
res.end('An error occurred while reading the file.');
} else {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end(data);
}
});
}).listen(3000, () => {
console.log('Server is running on port 3000');
});
在这个示例中,fs.readFile()
方法是非阻塞的。一旦文件读取完成,它会调用提供的回调函数,将数据传递给 HTTP 响应对象。
4. 非阻塞 I/O 的优势:
- 更高的并发能力:由于 I/O 操作不会阻塞主线程,因此服务器可以处理更多的并发连接。
- 更高效的资源利用:避免了线程上下文切换带来的开销。
- 更好的响应性能:即使在高负载下也能保持良好的响应时间。
5. 注意事项:
- 虽然非阻塞 I/O 提高了性能,但不当的设计可能会导致回调地狱(例如:深度嵌套导致代码复杂)等问题,这可以通过使用 Promise、async/await 等现代 JavaScript 特性来缓解。
总的来说,非阻塞 I/O 是 Node.js 核心设计的一部分,它使得 Node.js 成为构建高性能网络服务的理想平台。
4、async/await 会把异步变成同步?
async/await
并不会真正把异步代码转换成同步执行,而是提供了一种让异步代码看起来像同步代码一样编写的语法糖。它使得异步操作更加简洁和易读,同时也避免了回调地狱的问题。
1. async/await 的工作原理:
-
async 函数:
async
关键字定义了一个特殊的函数,该函数总是返回一个 Promise。即使函数体内的代码没有显式返回一个 Promise,也会隐式地返回一个解析为undefined
的 Promise。
-
await 表达式:
await
关键字只能在async
函数内部使用,用于等待一个 Promise 完成(解析或拒绝)。await
会暂停async
函数的执行,直到 Promise 解析或拒绝,然后返回 Promise 的结果值。
2. 重要概念:
-
非阻塞性:
- 尽管
async/await
让代码看起来像顺序执行,但实际上它仍然是非阻塞的。当await
一个 Promise 时,async
函数的执行会被挂起,而不是阻塞主线程。这意味着其他异步操作仍然可以在await
的 Promise 完成之前继续执行。
- 尽管
-
错误处理:
async/await
提供了一种使用try...catch
结构来处理错误的方式。当await
的 Promise 被拒绝时,会抛出一个错误,这个错误可以在catch
块中被捕获。
3. 总结:
async/await
使得异步代码的编写更加简洁和易于理解,但它并没有改变异步操作的本质。这些操作仍然在后台异步执行,不会阻塞主线程。- 使用
async/await
的代码看起来像是同步执行的,但实际上它仍然遵循事件循环和异步 I/O 的原则。 - 通过使用
async/await
,你可以更好地控制异步流程,并使用传统的错误处理机制来捕获和处理错误。
总之,async/await
不会把异步代码变成同步执行,而是通过语法上的优化让异步代码更易于编写和理解。
5、常用package
- typeorm
- RxJS,Reactive Extensions for JavaScript,是一个用于处理响应式编程的库,它提供了一种强大的模式来组合异步数据流,并且能够干净、优雅地处理这些数据。RxJS 主要基于观察者模式和行为模式,允许开发者以声明式的方式编写异步代码。
firstValueFrom
是 RxJS 6.5.0 版本之后引入的一个实用函数,它从一个Observable
中提取第一个值并将其作为 Promise 返回。 - redis
- moment,
moment().utc().toDate();
- log4js
- log4js-cloudwatch-appender
-
lodash /ˈloʊ.dæʃ/
- dotenv
- crypto-js
- axios /ˈæksi.ɒs/
- @nestjs/jwt