为什么 node.js 是异步的?

IT技术 javascript multithreading node.js asynchronous synchronous
2021-02-22 23:44:23

没有人真正问过这个问题(从我得到的所有“建议”以及在我问这里之前的搜索)。

那么为什么 node.js 是异步的呢?

根据我经过一些研究得出的结论:

像 PHP 和 Python 这样的语言是脚本语言(我可能对脚本语言的实际语言有误解)而 JavaScript 不是。(我想这是因为 JS 无法编译?)

Node.js 在单线程上运行,而脚本语言使用多线程。

异步意味着无状态并且连接是持久的,而同步则(几乎)相反。

也许答案可以在上面提到的某个地方找到,但我仍然不确定。

我与此主题相关的第二个也是最后一个问题是:

JavaScript 能否成为同步语言?

附注。我知道你们中的一些人会问“为什么要使 JS 同步?” 在你的答案中,但事实是我没有。我只是问这些类型的问题,因为我确信除了我自己之外,还有更多的人考虑过这些问题。

3个回答

Node.js 在单线程上运行,而脚本语言使用多线程。

技术上不是。Node.js 使用多个线程,但只有一个执行线程。后台线程用于处理 IO 以使所有异步优点工作。有效地处理线程是一个巨大的痛苦,所以下一个最佳选择是在事件循环中运行,这样代码就可以在后台线程被 IO 阻塞的情况下运行。

异步意味着无状态并且连接是持久的,而同步则(几乎)相反。

不必要。您可以非常轻松地在异步系统中保留状态。例如,在 Javascript 中,您可以使用bind()将 a 绑定this到函数,从而在函数返回时显式保留状态:

function State() {
    // make sure that whenever doStuff is called it maintains its state
    this.doStuff = this.doStuff.bind(this);
}
State.prototype.doStuff = function () {
};

异步意味着不等待操作完成,而是注册一个监听器。这在其他语言中一直发生,尤其是任何需要接受用户输入的语言。例如,在 Java GUI 中,您不会阻止等待用户按下按钮,而是向 GUI 注册一个侦听器。

我与此主题相关的第二个也是最后一个问题是:

JavaScript 能否成为同步语言?

从技术上讲,所有语言都是同步的,甚至 Javascript。然而,Javascript 在异步设计中工作得更好,因为它被设计为单线程。

基本上有两种类型的程序:

  • CPU 受限——让它跑得更快的唯一方法是获得更多的 CPU 时间
  • IO 限制-花费大量时间等待数据,因此更快的处理器无关紧要

视频游戏、数字处理器和编译器受 CPU 限制,而 Web 服务器和 GUI 通常受 IO 限制。Javascript 相对较慢(因为它有多复杂),因此它无法在 CPU 密集型场景中竞争(相信我,我已经编写了我对 CPU 密集型 Javascript 的公平份额)。

Javascript 不是根据类和对象进行编码,而是根据可以串在一起的简单函数进行编码。这在异步设计中非常有效,因为可以编写算法以在数据传入时以增量方式处理数据。IO(尤其是网络 IO)非常慢,因此数据包之间有相当多的时间。

例子

假设您有 1000 个实时连接,每个连接每毫秒传送一个数据包,处理每个数据包需要 1 微秒(非常合理)。我们还假设每个连接发送 5 个数据包。

在单线程、同步的应用程序中,每个连接都将被串行处理。花费的总时间是 (5*1 + 5*.001) * 1000 毫秒,或 ~5005 毫秒。

在单线程、异步应用程序中,每个连接都将并行处理。由于每个数据包需要 1 毫秒,而处理每个数据包需要 0.001 毫秒,因此我们可以处理数据包之间的每个连接的数据包,因此我们的公式变为:1000*.001 + 5*1 毫秒,或 ~6 毫秒。

此问题的传统解决方案是创建更多线程。这解决了 IO 问题,但是当连接数增加时,内存使用量(线程消耗大量内存)和 CPU 使用量(将 100 个线程复用到 1 个核心上比 1 个线程在 1 个核心上更难)也随之增加。

但是,也有缺点。如果您的 Web 应用程序碰巧还需要进行一些繁重的数字运算,那么您就是 SOL,因为在处理数字时,连接需要等待。线程解决了这个问题,因为当数据准备好等待 IO 的线程时,操作系统可以换出 CPU 密集型任务。此外,node.js 绑定到单个内核,因此除非启动多个实例和代理请求,否则您无法利用多核处理器。

Javascript 不会编译成任何东西。它在运行时被“评估”,就像 PHP 和 Ruby 一样。因此它是一种类似于 PHP/Ruby 的脚本语言。(它的正式名称实际上是 ECMAScript)。

Node 遵循的“模型”与 PHP/Ruby 有点不同。Node.js 使用一个“事件循环”(单线程),它的一个目标是接收网络请求并非常快速地处理它们,如果由于某种原因它遇到一个需要一段时间的操作(API 请求、数据库查询——基本上任何涉及 IO(输入/输出)的东西)它将它传递给后台“工作”线程并在工作线程等待长任务完成时开始做其他事情。当这种情况发生时,主要的“事件循环”将获取结果并继续处理它们。

PHP/Ruby 遵循线程模型。本质上,对于每个传入的网络请求,应用程序服务器都会启动一个孤立的线程或进程来处理请求。这并不能很好地扩展,与此模型相比,Node 的方法被认为是其核心优势之一。

异步意味着无状态并且连接是持久的,而同步则(几乎)相反。

不会。同步指令按自然顺序完成,从头到尾。异步指令是指如果程序流程中的某个步骤花费的时间相对较长,则程序将继续执行操作,并在完成后简单地返回到该操作。

JavaScript 能否成为同步语言?

JavaScript 中的某些操作是同步的。其他是异步的。例如:


阻塞操作:

for(var k = 0; k < 1; k = k - 1;){
  alert('this will quickly get annoying and the loop will block execution')
alert('this is blocked and will never happen because the above loop is infinite');

异步:

jQuery.get('/foo', function (result) { alert('This will occur 2nd, asynchronously'); });
alert('This will occur 1st. The above operation was skipped over and execution continued until the above operation completes.');
@flob JS 只编译 PHP 或 Python。V8 是一个很棒的运行时,但是你给它纯文本脚本,而不是编译的二进制文件。
2021-04-26 23:44:23
@BaileyS 它的行为类似于解释器,但它的操作方式与编译器类似,通常称为 JIT编译器这有点灰色地带,但我认为称其为“编译”并非没有道理。
2021-05-08 23:44:23
@BaileyS 最好考虑编译和解释的实现,因为语言可以有多个。如果有人以某种方式为 PHP 编写了一个非常快的编译器,那么该语言仍然是相同的。所以我想我同意添加新编译器不会重新分类语言,但不是因为 JIT 编译器不是真正的编译器。
2021-05-09 23:44:23
@Fresheyeball “V8 在第一次执行时将 JavaScript 源代码直接编译成机器码。没有中间字节码,没有解释器。” 请参阅v8 设计文档 -> 动态机器代码生成
2021-05-14 23:44:23
Javascript 不会编译成任何东西。是错的。Node.js 使用 V8,其中包含一个 JIT 编译器,可将 Javascript 编译为机器代码。对于 PHP,有很多可以编译代码的解决方案Ruby 不评估源代码,而是在抽象语法树上运行。-> 语言本身并不意味着它是被解释的还是被编译的,但问题所涉及的具体实现并不是在经典意义上解释的。
2021-05-16 23:44:23

JavaScript 能否成为同步语言?

Javascript 不是“异步语言”;相反,node.js有很多异步API异步性是 API 的属性,而不是语言的属性。在 javascript 中可以轻松创建和传递函数,这使得传递回调函数变得很方便,这是在异步 API 中处理控制流的一种方式,但javascript没有任何本质上的异步Javascript 可以轻松支持同步 API。

为什么 node.js 是异步的?

Node.js 偏爱异步 API,因为它是单线程的。这允许它有效地管理自己的资源,但要求长时间运行的操作是非阻塞的,异步 API 是一种允许通过大量非阻塞操作控制流的方法。