Node.js 的精髓
Node.js 或 Node 有一小部分核心模块,通常称为 Node Core,它通过使用我们编写的应用程序公开为公共 Node API,或者我们可以说 Node Core 实现了公共 Node API。
节点核心中存在的模块的一些示例是:
- 为了使用文件系统,我们有一个fs模块。
- 对于网络,我们有一个http模块。
- 为了获取特定于操作系统的信息,我们有一个名为os的模块。
像这些一样,节点核心中有几十个模块,但其中大部分是为了支持节点的主要用例。 Node 主要通过回调、事件和流来处理其 I/O 操作。所以你需要了解这些概念。
在我们开始讨论上述概念之前,请确保您已经安装了 node.js。如果您在安装时遇到问题,可以参考我们的安装指南。
回调:回调是掌握 Node.js 需要了解的最重要的基础知识之一。在此之前,让我们看看为什么我们需要回调以及回调在 Node.js 中是如何工作的。
传统的 Web 服务器是同步工作的。这意味着当请求发送到服务器时,服务器会处理请求并提供响应。在处理期间,其他 I/O 操作必须等待当前进程完成,然后才能处理另一个请求。我们将这种阻塞 I/O 称为阻塞 I/O,因为新请求会被阻塞,直到当前进程完成。
Node 具有非阻塞 I/O 模型,因为 Node 在设计上是异步的。使用 Node 制作的服务器在收到请求时,处理它并像传统服务器一样返回响应。但是当请求处于处理期时,Node 服务器可以同时执行其他任务。
示例:创建一个新文件夹,然后在其中创建一个learn-callback.js文件和name.txt文件。我们的目标是向终端打印自定义的 hello 和循环模式。将您的姓名放入name.txt并保存文件。我们的文件中有“ GeeksforGeeks ”。
- 传统服务器的同步版本:
// Tell node we need to work with filesystem const fs = require("fs"); // Read the file contents "synchronously" in // string (utf-8) encoding const fileContents = fs.readFileSync("name.txt", "utf-8"); // Print to console console.log("Hello, ", fileContents); // Print pattern for (let i = 0; i < 5; i++) console.log(i);
输出:
Hello, GeeksforGeeks 0 1 2 3 4
- Node 异步版本:在保存文件的目录中打开终端。使用node learn-callback.js运行代码并观察输出。您将进入重点,但首先,请查看 Node 版本。
// Tell node we need to work with filesystem const fs = require("fs"); // Read the file contents "asynchronously" in // string (utf-8) encoding fs.readFile("name.txt", "utf-8", (error, fileContents) => { if (error) return error; else console.log("Hello, ", fileContents); }); // Print the pattern for (let i = 0; i < 5; i++) console.log(i);
输出:
0 1 2 3 4 Hello, GeeksforGeeks
说明:使用node learn-callback.js运行代码。你注意到输出的不同了吗?这是由于 Node 的非阻塞模型。在同步版本中,我们首先观察 hello,然后观察模式。我们发出读取name.txt文件的请求,处理文件,打印 hello,然后打印模式。在同步模型中,执行是顺序的,即从上到下的顺序。
在节点的异步版本中,当我们发出读取文件的请求时,文件开始处理,但在这种情况下,我们的程序可以在节点读取文件时同时执行其他任务。这节省了计算资源,并使节点 I/O 操作非常快。
在上面突出显示的代码中, fs.readFile告诉节点以 utf-8 编码读取name.txt文件。 fs.readFile的第三个参数是回调。回调是在特定进程完成时执行的函数。当文件被读取时,节点是空闲的,它在fs.readFile函数之后执行下一行代码,这在我们的例子中恰好是一个循环,所以循环在读取过程中被执行,我们在终端。读取完成后,回调函数执行,并在模式之后在终端中打印 hello。
因此,回调是在进程完成执行后稍后执行的函数,并且在此期间节点可以自由地执行其他任务。请记住,回调不是节点的特殊功能。实际上,它们是内置在 JavaScript 中的。 Node 只是巧妙地使用它来实现非阻塞 I/O 特性。
事件:您可以考虑诸如“当 X 发生在 Y 上”之类的事件。因此,在这个类比中,“X”是 Node 发出的事件,“Y”是等待“X”信号完成其工作的侦听器。让我们编写一个小程序来掌握这个概念。
- 示例:此示例说明事件。运行这段代码,看看你是否得到正确的输出。
// Require "events"; give us access to EventEmitter class // EventEmitter class has all the event related methods in it const EventEmitter = require("events"); // Create an instance of the EventEmitter class const ourEmitter = new EventEmitter(); // Create an event listener - listens for the "GfG opened" event // Event listeners always keep its ear open; it never sleeps // Means it'll keep on listening for the event throughout the code // It'll execute the callback function when "GfG opened" event is emitted ourEmitter.on("GfG opened", (error) => { if (error) return error; else console.log("Let's learn computer science concepts."); }); // Emit event or send a signal that "GfG opened" has happened ourEmitter.emit("GfG opened");
输出:
Let's learn computer science concepts.
说明:当您发出“GfG 已打开”事件时,我们有一个事件监听器执行回调函数,该回调函数将消息打印到控制台。现在让我们看看当我们把ourEmitter.emit(“GfG opens”);在事件监听器之前。
- 放置ourEmitter.emit(“GfG打开”)的程序;在事件监听器之前:
... // Emit "GfG opened" ourEmitter.emit("GfG opened"); // Create an event listener ourEmitter.on("GfG opened", (error) => { if (error) return error; else console.log("Let's learn computer science concepts."); ...
输出:节点事件 API 说:
"When the EventEmitter object emits an event, all of the functions attached to that specific event are called synchronously"
这意味着当节点发出“GfG 已打开”事件时,节点会检查是否有人在侦听此事件,但节点还不知道侦听器,因为侦听器在发出命令之后。节点事件
.emit
命令无法检查在发出命令之后出现的侦听器,因为它是同步的。因此,在处理事件时,代码的顺序很重要。经验法则是:先听然后发出。首先,创建一个侦听器,然后发出事件。事件对于创建需要知道新玩家何时连接或断开连接、移动、射击、死亡等的游戏服务器非常有用。此外,事件也大量用于创建您希望向听众广播消息的聊天室。
Streams:读取和写入数据有两种方法: Buffered和Streams 。在缓冲方法中,必须在写入过程开始之前读取整个数据。但是流的效率要高得多。流读取一块数据,此时另一个流可以继续写入前一个数据块。所以 node.js异步处理数据——并行执行任务。
溪流配有管道。基本上管道的作用是它们获取流的输出,并且可以通过管道将输出发送到另一个流,该管道成为该新流的输入。这给节点带来了惊人的力量。流具有时间效率,因为它们不会浪费时间等待所有读取同时发生,而是我们不断地同时读取和写入。是的,正如他们所说,我们正在异步进行。
此外,流在空间上是有效的(节省内存空间)。假设我们必须读取一个 100 MB 的文件并将其写入某处,并且我们有一个 50 MB 的缓冲区。缓冲方法首先需要读取整个数据,然后才能开始写入。在读取过程开始时使用缓冲方法,一旦超过 50 MB 标记,我们的缓冲区最终会泄漏。
但是我们可以使用流来读取 50 MB 的数据块,写入该数据,然后清除该缓冲区,然后再继续读取下一个 50 MB。所以在流的情况下不会有泄漏。