为什么Node.JS中的函数和回调都是非阻塞的?

Inc*_*982 11 nonblocking node.js

新手对Node的理解是,如果我重写同步或内联代码来利用函数/回调,我可以确保我的代码是非阻塞的.我很好奇这是如何在事件堆栈方面工作的.这里的简单示例:不了解回调 - Stackoverflow是否会阻止:

var post = db.query("select * from posts where id = 1");
doSomethingWithPost(post)
doSomethingElse();
Run Code Online (Sandbox Code Playgroud)

虽然这不会:

callback = function(post){
doSomethingWithPost(post)
}

db.query("select * from posts where id = 1",callback);
doSomethingElse();
Run Code Online (Sandbox Code Playgroud)

好的,我知道我们应该使用回调.但就事件堆栈而言,为什么这样做呢?Javascript是单线程..在第一个示例行中,一个使用昂贵的阻塞I/O操作.在第一行完成之前,第2行无法执行.这是因为第2行需要第1行的信息吗?或者是因为I/O事件只是从根本上阻止了操作,这意味着他们抓住了控制权并且在完成之前不再回复...

在第二个例子中,昂贵的I/O已被移动到一个函数中,我们现在有一个回调函数.当然,在完成I/O之前,回调不能执行.这不会改变.因此,执行1到2之间所需的时间差异必须主要是第二个请求到达服务器时会发生的情况.

如果第二个请求遇到示例一,它将无法处理,直到请求1由于阻塞操作而完成..但在示例二中..将操作移动到函数中会自动生成子进程或充当多线程?如果Javscript是单线程的,那么除非有某种方式进行并行处理,否则这仍然会产生问题.如果我们使用非阻塞技术(如子进程等),函数/回调是否仅保证是非阻塞的. .

Lin*_*iel 19

想象一下,你正在一家面包店经营收银机.您按顺序和同步处理客户,如下所示:

  1. 点单
  2. 告诉面包师烤面包
  3. 等到面包烤好
  4. 收钱
  5. 提供面包
  6. GOTO 1 - 下一位客户

那将是非常缓慢的.现在,尝试按顺序接受订单,但异步处理客户:

  1. 点单
  2. 告诉面包师烤面包,完成后通知你.收到通知时:
    1. 收钱
    2. 提供面包
  3. GOTO 1 - 下一位客户

更新:我重构了上面的内容,所以它更像是一个回调.您,收银员,在将订单发给面包师后会立即点击第3步.当面包师通知您面包已准备好时,您将点击步骤2.1.

通过这种方式,您仍然可以提供尽可能多的面包 - 您只能出售面包烘焙所需的面包.但是您可以以更有效的方式与您的客户打交道,因为您不是懒得等待订单回来,而是开始处理下一个客户.

现在,你可以对此采取各种各样的想法,并预先收取钱,并告诉客户拿起桌子另一端的面包,或类似的东西.我认为星巴克以这种方式"非常".收银员接受订单,发出一些要求的东西,并告诉客户等待所有东西都站在取货区域.超高效.

现在,想象一下你的朋友开始经营另一个收银机.他遵循你的异步示例.您可以更快地处理更多客户!请注意,您唯一要做的就是将您的朋友放在那里并给他您的工作流程.

您和您的朋友是两个并行运行的单线程事件循环.这类似于两个获取请求的node.js进程.你没有必要做任何复杂的并行化,你只需再运行一个事件循环.

所以,不,"将操作转移到函数中"不会"自动生成子进程".它们更类似于警报 - 当它完成时,通知我并让我在此时接听,"这一点"是你回调中的代码.但是回调仍然会在同一个进程和同一个线程中执行.

现在,node.js还为IO运行内部线程池.这是抽象的远离你:继续面包店比喻,让我们说你有一个面包师的"面包师池" - 对你,站在收银台,你不必知道这一点.你只需给他们订单("一个酵母面包"),并在收到通知表明它完成后发出订单.但面包师正在他们自己的"面包师池"中平行烘烤他们的面包.