by Pablo Regen
通过帕勃罗·雷根(Pablo Regen)
You’ve probably read these sentences before…
您可能已经读过这些句子了……
Node.js is a JavaScript runtime built on Chrome’s V8 JavaScript engine
Node.js是基于Chrome V8 JavaScript引擎构建JavaScript运行时
Node.js uses an event-driven, asynchronous non-blocking I/O model
Node.js使用事件驱动的异步非阻塞I / O模型
Node.js operates on a single thread event loop
Node.js在单线程事件循环上运行
… and were left wondering what all this meant. Hopefully by the end of this article you’ll have a better understanding about these terms as well as about what Node is, how it works, and why and when is a good idea to use it.
……让他们想知道这是什么意思。 希望到本文结尾时,您将对这些术语以及什么是Node,它如何工作以及为什么以及何时使用它是个好主意有更好的理解。
Let’s start by going over the terminology.
让我们从术语开始。
Short for input/output, I/O refers primarily to the program’s interaction with the system’s disk and network. Examples of I/O operations include reading/writing data from/to a disk, making HTTP requests, and talking to databases. They are very slow compared to accessing memory (RAM) or doing work on the CPU.
I / O是输入/输出的缩写,主要是指程序与系统磁盘和网络的交互。 I / O操作的示例包括从磁盘读取数据/向磁盘写入数据,发出HTTP请求以及与数据库对话。 与访问内存(RAM)或在CPU上进行工作相比,它们非常慢。
Synchronous (or sync) execution usually refers to code executing in sequence. In sync programming, the program is executed line by line, one line at a time. Each time a function is called, the program execution waits until that function returns before continuing to the next line of code.
同步 (或同步)执行通常是指按顺序执行的代码。 在同步编程中,程序一行一行地执行,一次一行。 每次调用一个函数时,程序执行都会等到该函数返回后再继续执行下一行代码。
Asynchronous (or async) execution refers to execution that doesn’t run in the sequence it appears in the code. In async programming the program doesn’t wait for the task to complete and can move on to the next task.
异步 (或异步)执行是指未按照代码中出现的顺序运行。 在异步编程中,程序不会等待任务完成,而是可以继续执行下一个任务。
In the following example, the sync operation causes the alerts to fire in sequence. In the async operation, while alert(2) appears to execute second, it doesn’t.
在以下示例中,同步操作使警报按顺序触发。 在异步操作中,当alert(2)似乎第二次执行时,它没有执行。
// Synchronous: 1,2,3
alert(1);
alert(2);
alert(3);
// Asynchronous: 1,3,2
alert(1);
setTimeout(() => alert(2), 0);
alert(3);
An async operation is often I/O related, although setTimeout
is an example of something that isn’t I/O but still async. Generally speaking, anything computation-related is sync and anything input/output/timing-related is async. The reason for I/O operations to be done asynchronously is that they are very slow and would block further execution of code otherwise.
异步操作通常与I / O相关,尽管setTimeout
是不是I / O但仍然异步的示例。 一般来说,任何与计算有关的都是同步,而与输入/输出/定时有关的都是异步。 异步执行I / O操作的原因是它们非常慢,否则会阻止代码的进一步执行。
Blocking refers to operations that block further execution until that operation finishes while non-blocking refers to code that doesn’t block execution. Or as Node.js docs puts it, blocking is when the execution of additional JavaScript in the Node.js process must wait until a non-JavaScript operation completes.
阻塞是指阻止进一步执行直到该操作完成的操作,而非阻塞是指不阻止执行的代码。 或如Node.js文档所述,阻塞是指Node.js进程中其他JavaScript的执行必须等到非JavaScript操作完成后才能执行。
Blocking methods execute synchronously while non-blocking methods execute asynchronously.
阻塞方法同步执行,而非阻塞方法异步执行。
// Blocking
const fs = require('fs');
const data = fs.readFileSync('/file.md'); // blocks here until file is read
console.log(data);
moreWork(); // will run after console.log
// Non-blocking
const fs = require('fs');
fs.readFile('/file.md', (err, data) => {
if (err) throw err;
console.log(data);
});
moreWork(); // will run before console.log
In the first example above, console.log
will be called before moreWork()
. In the second example fs.readFile()
is non-blocking so JavaScript execution can continue and moreWork()
will be called first.
在上面的第一个示例中, console.log
将在moreWork()
之前moreWork()
。 在第二个示例中, fs.readFile()
是非阻塞的,因此JavaScript可以继续执行,并且将首先调用moreWork()
。
In Node, non-blocking primarily refers to I/O operations, and JavaScript that exhibits poor performance due to being CPU intensive rather than waiting on a non-JavaScript operation, such as I/O, isn’t typically referred to as blocking.
在Node中,非阻塞主要指I / O操作,并且由于占用大量CPU而不是等待诸如I / O之类的非JavaScript操作而表现出较差的性能JavaScript通常不称为阻塞。
All of the I/O methods in the Node.js standard library provide async versions, which are non-blocking, and accept callback functions. Some methods also have blocking counterparts, which have names that end with Sync.
Node.js标准库中的所有I / O方法都提供异步版本,这些版本是非阻塞的,并接受回调函数。 某些方法还具有阻塞的对应项,它们的名称以Sync结尾。
Non-blocking I/O operations allow a single process to serve multiple requests at the same time. Instead of the process being blocked and waiting for I/O operations to complete, the I/O operations are delegated to the system, so that the process can execute the next piece of code. Non-blocking I/O operations provide a callback function that is called when the operation is completed.
非阻塞I / O操作允许单个进程同时处理多个请求。 无需阻塞进程并等待I / O操作完成,而是将I / O操作委托给系统,以便该进程可以执行下一段代码。 非阻塞I / O操作提供了在操作完成时调用的回调函数。
A callback is a function passed as an argument into another function, which can then be invoked (called back) inside the outer function to complete some kind of action at a convenient time. The invocation may be immediate (sync callback) or it might happen at a later time (async callback).
回调是一个作为参数传递给另一个函数的函数,然后可以在外部函数内部调用(称为回叫)以在方便的时间完成某种操作。 调用可以是立即的(同步回调),也可以在以后的时间(异步回调)进行。
// Sync callback
function greetings(callback) {
callback();
}
greetings(() => { console.log('Hi'); });
moreWork(); // will run after console.log
// Async callback
const fs = require('fs');
fs.readFile('/file.md', function callback(err, data) { // fs.readFile is an async method provided by Node
if (err) throw err;
console.log(data);
});
moreWork(); // will run before console.log
In the first example, the callback function is called immediately within the outer greetings function and logs to the console before moreWork()
proceeds.
在第一个示例中,回调函数在外部greetings函数内立即被调用,并在moreWork()
进行之前登录到控制台。
In the second example, fs.readFile (an async method provided by Node) reads the file and when it finishes it calls the callback function with an error or the file content. In the meantime the program can continue code execution.
在第二个示例中,fs.readFile(Node提供的异步方法)读取文件,并在完成时调用带有错误或文件内容的回调函数。 同时,程序可以继续执行代码。
An async callback may be called when an event happens or when a task completes. It prevents blocking by allowing other code to be executed in the meantime.
当事件发生或任务完成时,可以调用异步回调。 它通过允许同时执行其他代码来防止阻塞。
Instead of the code reading top to bottom procedurally, async programs may execute different functions at different times based on the order and speed that earlier functions like http requests or file system reads happen. They are used when you don’t know when some async operation will complete.
代替程序从头到尾地读取代码,异步程序可以根据诸如HTTP请求或文件系统读取之类的早期功能发生的顺序和速度,在不同的时间执行不同的功能。 当您不知道何时完成某些异步操作时,将使用它们。
You should avoid “callback hell”, a situation where callbacks are nested within other callbacks several levels deep, making the code difficult to understand, maintain and debug.
您应避免使用“ 回调地狱 ”,这种情况是回调嵌套在其他回调中,嵌套在多个级别中,使代码难以理解,维护和调试。
Events are actions generated by the user or the system, like a click, a completed file download, or a hardware or software error.
事件是用户或系统生成的操作,例如单击,完成的文件下载或硬件或软件错误。
Event-driven programming is a programming paradigm in which the flow of the program is determined by events. An event-driven program performs actions in response to events. When an event occurs it triggers a callback function.
事件驱动编程是一种编程范例,其中程序的流程由事件确定。 事件驱动程序执行响应事件的操作。 当事件发生时,它将触发回调函数。
Now, let’s try to understand Node and see how all these relate to it.
现在,让我们尝试了解Node并了解所有这些与Node的关系。
Simply put, Node.js is a platform that executes server-side JavaScript programs that can communicate with I/O sources like networks and file systems.
简而言之, Node.js是执行服务器端JavaScript程序的平台,该程序可以与网络和文件系统等I / O源进行通信。
When Ryan Dahl created Node in 2009 he argued that I/O was being handled incorrectly, blocking the entire process due to synchronous programming.
当Ryan Dahl在2009年创建Node时,他认为I / O的处理不正确,由于同步编程而阻塞了整个过程。
Traditional web-serving techniques use the thread model, meaning one thread for each request. Since in an I/O operation the request spends most of the time waiting for it to complete, intensive I/O scenarios entail a large amount of unused resources (such as memory) linked to these threads. Therefore the “one thread per request” model for a server doesn’t scale well.
传统的Web服务技术使用线程模型,这意味着每个请求一个线程。 由于在I / O操作中,请求将花费大部分时间等待其完成,因此密集型I / O方案需要链接到这些线程的大量未使用资源(例如内存)。 因此,服务器的“每个请求一个线程”模型无法很好地扩展。
Dahl argued that software should be able to multi-task and proposed eliminating the time spent waiting for I/O results to come back. Instead of the thread model, he said the right way to handle several concurrent connections was to have a single-thread, an event loop and non-blocking I/Os. For example, when you make a query to a database, instead of waiting for the response you give it a callback so your execution can run through that statement and continue doing other things. When the results come back you can execute the callback.
达尔认为软件应该能够执行多任务,并建议消除等待I / O结果返回所花费的时间。 他说,处理线程并发连接的正确方法是使用单线程,事件循环和无阻塞的I / O,而不是线程模型。 例如,当您对数据库进行查询时,无需等待响应,而是给它回调,以便您的执行可以贯穿该语句并继续执行其他操作。 结果返回时,您可以执行回调。
The event loop is what allows Node.js to perform non-blocking I/O operations despite the fact that JavaScript is single-threaded. The loop, which runs on the same thread as the JavaScript code, grabs a task from the code and executes it. If the task is async or an I/O operation the loop offloads it to the system kernel, like in the case for new connections to the server, or to a thread pool, like file system related operations. The loop then grabs the next task and executes it.
尽管JavaScript是单线程的,但事件循环使Node.js能够执行非阻塞I / O操作。 该循环与JavaScript代码在同一线程上运行,该循环从代码中获取任务并执行该任务。 如果任务是异步的或I / O操作,则循环会将其分流到系统内核,例如与服务器或线程池的新连接(例如与文件系统相关的操作)。 循环然后获取下一个任务并执行它。
Since most modern kernels are multi-threaded, they can handle multiple operations executing in the background. When one of these operations completes (this is an event), the kernel tells Node.js so that the appropriate callback (the one that depended on the operation completing) may be added to the poll queue to eventually be executed.
由于大多数现代内核都是多线程的,因此它们可以处理在后台执行的多个操作。 当这些操作之一完成时(这是一个事件),内核告诉Node.js,以便可以将适当的回调(取决于操作完成的回调)添加到轮询队列中以最终执行。
Node keeps track of unfinished async operations, and the event loop keeps looping to check if they are finished until all of them are.
Node跟踪未完成的异步操作,事件循环不断循环检查它们是否已完成,直到全部完成为止。
To accommodate the single-threaded event loop, Node.js uses the libuv library, which, in turn, uses a fixed-sized thread pool that handles the execution of some of the non-blocking asynchronous I/O operations in parallel. The main thread call functions post tasks to the shared task queue, which threads in the thread pool pull and execute.
为了适应单线程事件循环,Node.js使用libuv库,该库又使用固定大小的线程池来处理并行执行的一些非阻塞异步I / O操作。 主线程调用函数将任务发布到共享任务队列,该任务在线程池中的线程拉出并执行。
Inherently non-blocking system functions such as networking translate to kernel-side non-blocking sockets, while inherently blocking system functions such as file I/O run in a blocking way on their own threads. When a thread in the thread pool completes a task, it informs the main thread of this, which in turn, wakes up and executes the registered callback.
诸如网络之类的固有非阻塞系统功能转换为内核侧非阻塞套接字,而诸如文件I / O之类的固有阻塞系统功能以阻塞方式在其自己的线程上运行。 当线程池中的线程完成任务时,它将通知主线程,该线程又唤醒并执行注册的回调。
The above image is taken from Philip Roberts’ presentation at JSConf EU: What the heck is the event loop anyway? I recommend watching the full video to get a high level idea about how the event loop works.
上面的图片摘自Philip Roberts在JSConf EU上的演讲: 无论如何,事件循环到底是什么呢? 我建议观看完整的视频,以全面了解事件循环的工作原理。
The diagram explains how the event loop works with the browser but it looks basically identical for Node. Instead of web APIs we would have Node APIs.
该图说明了事件循环如何与浏览器一起工作,但对于Node而言,它看起来基本上是相同的。 取而代之的是Web API,而不是Web API。
According to the presentation, the call stack (aka execution stack or “the stack”) is a data structure which records where in the program we are. If we step into a function, we put something onto the stack. If we return from a function, we pop it off the top of the stack.
根据演示,调用堆栈(也称为执行堆栈或“堆栈”)是一种数据结构,用于记录我们在程序中的位置。 如果进入函数,则会将某些东西放到堆栈中。 如果从函数返回,则将其弹出堆栈顶部。
This is how the code in the diagram is processed when we run it:
这是我们运行图表时如何处理图中的代码:
Push main()
onto the stack (the file itself)
将main()
推入堆栈(文件本身)
Push console.log(‘Hi’);
onto the stack, which executes immediately logging “Hi” to the console and gets popped off the stack
推送console.log('Hi');
到堆栈上,立即执行将“ Hi”登录到控制台并从堆栈弹出
Push setTimeout(cb, 5000)
onto the stack. setTimeout is an API provided by the browser (on the backend it would be a Node API). When setTimeout is called with the callback function and delay arguments, the browser kicks off a timer with the delay time
将setTimeout(cb, 5000)
推入堆栈。 setTimeout是浏览器提供的API(在后端它将是Node API)。 当使用回调函数和delay参数调用setTimeout时,浏览器将启动一个带有延迟时间的计时器
The setTimeout
call is completed and gets popped off the stack
setTimeout
调用已完成,并从堆栈中弹出
Push console.log(‘JSConfEU’);
onto the stack, which executes immediately logging “JSConfEU” to the console and gets popped off the stack
推送console.log('JSConfEU');
到堆栈上,立即执行将“ JSConfEU”登录到控制台并从堆栈中弹出
main()
gets popped off the stack
main()
从堆栈弹出
If you want to go even deeper into the details on how Node.js, libuv, the event loop and the thread pool work, I suggest checking the resources on the reference section at the end, in particular this, this and this along with the Node docs.
如果您想更深入地了解Node.js,libuv,事件循环和线程池的工作方式,我建议最后检查参考部分的资源,尤其是this , this和this以及节点文档 。
Since almost no function in Node directly performs I/O, the process never blocks (I/O operations are offloaded and executed asynchronously in the system), making it a good choice to develop highly scalable systems.
由于Node中几乎没有功能直接执行I / O,因此该过程永远不会阻塞(I / O操作被卸载并在系统中异步执行),这使其成为开发高度可扩展系统的理想选择。
Due to its event-driven, single-threaded event loop and asynchronous non-blocking I/O model, Node.js performs best on intense I/O applications requiring speed and scalability with lots of concurrent connections, like video & audio streaming, real-time apps, live chats, gaming apps, collaboration tools, or stock exchange software.
由于其事件驱动,单线程事件循环和异步非阻塞I / O模型,Node.js在需要速度和可伸缩性且需要大量并发连接(例如视频和音频流,实时)的密集型I / O应用程序上表现最佳时应用,实时聊天,游戏应用,协作工具或证券交易所软件。
Node.js may not be the right choice for CPU intensive operations. Instead the traditional thread model may perform better.
对于CPU密集型操作,Node.js可能不是正确的选择。 取而代之的是传统线程模型可能会表现更好。
npm is the default package manager for Node.js and it gets installed into the system when Node.js is installed. It can manage packages that are local dependencies of a particular project, as well as globally-installed JavaScript tools.
npm是Node.js的默认软件包管理器,在安装Node.js时会被安装到系统中。 它可以管理作为特定项目的本地依赖项的程序包,以及全局安装JavaScript工具。
www.npmjs.com hosts thousands of free libraries to download and use in your program to make development faster and more efficient. However, since anybody can create libraries and there’s no vetting process for submission, you have to be careful about low quality, insecure, or malicious ones. npm relies on user reports to take down packages if they violate policies, and to help you decide, it includes statistics like number of downloads and number of depending packages.
www.npmjs.com托管了数千个免费库,供您下载并在您的程序中使用,以使开发更快,更高效。 但是,由于任何人都可以创建库并且没有提交审核程序,因此您必须注意低质量,不安全或恶意的库。 如果用户报告违反了政策,npm会依赖用户报告来删除软件包,并帮助您做出决定,其中包括诸如下载数量和依赖软件包数量之类的统计信息。
Start by installing Node on your computer if you don’t have it already. The easiest way is to visit nodejs.org and click to download it. Unless you want or need to have access to the latest features, download the LTS (Long Term Support) version for you operating system.
首先在计算机上安装Node(如果尚未安装)。 最简单的方法是访问nodejs.org并单击下载。 除非您希望或需要访问最新功能,否则请为您的操作系统下载LTS(长期支持)版本。
You run a Node application from your computer’s terminal. For example make a file “app.js” and add console.log(‘Hi’);
to it. On your terminal change the directory to the folder where this file belongs to and run node app.js
. It will log “Hi” to the console. ?
您从计算机的终端运行Node应用程序。 例如,创建一个文件“ app.js”并添加console.log('Hi');
;。 对此。 在终端上,将目录更改为该文件所属的文件夹,然后运行node app.js
它将“ Hi”登录到控制台。 ?
Here are some of the interesting resources I reviewed during the writing of the article.
这是我在撰写本文时回顾的一些有趣的资源。
Node.js presentations by its author:
作者的Node.js演示文稿:
Original Node.js presentation by Ryan Dahl at JSConf 2009
Ryan Dahl在JSConf 2009上的原始Node.js演示文稿
10 Things I Regret About Node.js by Ryan Dahl at JSConf EU 2018
Ryan Dahl在JSConf EU 2018上对Node.js感到遗憾的十件事
Node, the event loop and the libuv library presentations:
节点,事件循环和libuv库演示:
What the heck is the event loop anyway? by Philip Roberts at JSConf EU
无论如何,事件循环到底是什么? Philip Roberts在JSConf EU上发表
Node.js Explained by Jeff Kunkle
Jeff Kunkle 解释的Node.js
In The Loop by Jake Archibald at JSConf Asia 2018
杰克·阿奇博尔德(Jake Archibald)在JSConf Asia 2018上的`` 在循环中''
Everything You Need to Know About Node.js Event Loop by Bert Belder
您需要了解的有关Node.js事件循环的所有内容 ,作者Bert Belder
A deep dive into libuv by Saul Ibarra Coretge at NodeConf EU 2016
Saul Ibarra Coretge在NodeConf EU 2016上深入探讨libuv
Node documents:
节点文件:
About Node.js
关于Node.js
The Node.js Event Loop, Timers, and process.nextTick()
Node.js事件循环,计时器和process.nextTick()
Overview of Blocking vs Non-Blocking
阻塞与非阻塞概述
Additional resources:
其他资源:
Art of Node by Max Ogden
Max Ogden 的节点艺术
Callback hell by Max Ogden
Max Ogden的回调地狱
What is non-blocking or asynchronous I/O in Node.js? on Stack Overflow
Node.js中的非阻塞或异步I / O是什么? 堆栈溢出
Event driven programming on Wikipedia
维基百科上的事件驱动编程
Node.js on Wikipedia
Wikipedia上的Node.js
Thread on Wikipedia
维基百科上的主题
libuv
libuv
Thanks for reading.
谢谢阅读。
翻译自: https://www.freecodecamp.org/news/node-js-what-when-where-why-how-ab8424886e2/