一尘不染

为什么Node.JS中的函数和回调没有阻塞?

node.js

对Node的新手理解是,如果我重新编写同步代码或内联代码以利用函数/回调,则可以确保我的代码是非阻塞的。我很好奇这在事件堆栈方面的工作原理。
是它将阻塞:

var post = db.query("select * from posts where id = 1");
doSomethingWithPost(post)
doSomethingElse();

虽然这不会:

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

db.query("select * from posts where id = 1",callback);
doSomethingElse();

好的,我知道我们应该使用回调。但是就事件堆栈而言,这为何起作用?Javascript是单线程的。在第一个示例行中,它使用了昂贵且阻塞的I /
O操作。第1行完成之前,第2行无法执行。这是因为第2行需要来自第1行的信息吗?还是因为I /
O事件从根本上阻止了操作,这意味着它们抓住了控制权,直到完成才将其交还给他人…

在第二个示例中,昂贵的I / O已移到一个函数中,现在我们有了一个回调函数。当然,只有在I /
O完成后才能执行回调。这不会改变。因此,一两个之间执行时间的差异主要必须是第二个请求到达服务器时将发生的情况。

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


阅读 226

收藏
2020-07-07

共1个答案

一尘不染

想象您在面包店里操作收银机。您可以依次和同步地处理客户,如下所示:

  1. 点单
  2. 告诉贝克烤面包
  3. 等到烤面包
  4. 收费
  5. 送面包
  6. GOTO 1-下一个客户

那将非常慢。现在,试着依次接订单,但异步处理客户:

  1. 点单
  2. 告诉贝克烘烤面包,并在完成时通知您。收到通知时:
    1. 收费
    2. 送面包
  3. GOTO 1-下一个客户

更新: 我重构了上面,所以它更类似于回调。您(收银员)向面包师下达订单后将立即执行第3步。当面包师通知您面包准备好时,您将执行步骤2.1。

这样,您仍然可以提供尽可能多的面包-
您只能出售面包师可以烘烤的面包。但是,您可以以更有效的方式与客户打交道,因为您可以开始处理下一个客户,而不是闲着等待订单回来。

现在,您可以对此进行各种花哨的操作,并预先收取钱款,并告诉客户在桌子的另一端拿起面包,或类似的东西。我认为星巴克以这种方式相当“风骚”。收银员接订单,发出许多东西的要求,并告诉客户等到所有东西都放在提货区。超高效。

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

您和您的朋友是两个并行运行的单线程事件循环。这类似于两个接受请求的node.js进程。您不需要复杂的事情就可以并行化它,只需再运行一个事件循环即可。

因此,不,“将操作移入函数”不会“自动生成子进程”。它们更类似于警报-
完成警报后,请通知我,让我在这一点上接起来,“这一点”是您回调中的代码。但是回调仍将在相同的进程和相同的线程中执行。

现在,node.js还为IO运行内部线程池。这是从您这里抽象出来的:要继续进行面包店的类比,假设您有一个“面包店池”的面包店-
对您来说,站在收银机上,您不必了解这一点。您只需要给他们下订单(“一块酸面包”),并在收到通知后将其交付即可。但是,面包师们正在自己的“面包师池”中并行烘烤面包。

2020-07-07