同步和异步编程有什么区别(在 node.js 中)

IT技术 javascript node.js asynchronous synchronous
2021-01-26 14:31:39

我一直在阅读nodebeginner 并且遇到了以下两段代码。

第一个:

    var result = database.query("SELECT * FROM hugetable");
    console.log("Hello World");

第二个:

    database.query("SELECT * FROM hugetable", function(rows) {
       var result = rows;
    });
    console.log("Hello World");

我得到了他们应该做的事情,他们查询数据库以检索查询的答案。然后console.log('Hello world')

第一个应该是同步代码。第二个是异步代码。

这两件作品之间的区别对我来说非常模糊。输出会是什么?

谷歌异步编程也没有帮助我。

6个回答

不同的是,在第一个例子中,程序会在第一行阻塞。下一行 ( console.log) 将不得不等待。

第二个示例中console.log将在处理查询时执行。也就是说,查询将在后台处理,而您的程序正在做其他事情,一旦查询数据准备好,您就可以随心所欲地使用它。

所以,简而言之:第一个例子会阻塞,而第二个不会。

以下两个示例的输出:

// Example 1 - Synchronous (blocks)
var result = database.query("SELECT * FROM hugetable");
console.log("Query finished");
console.log("Next line");


// Example 2 - Asynchronous (doesn't block) 
database.query("SELECT * FROM hugetable", function(result) {
    console.log("Query finished");
});
console.log("Next line");

将是:

  1. Query finished
    Next line
  2. Next line
    Query finished

注意
虽然 Node 本身是单线程的,但有一些任务可以并行运行。例如,文件系统操作发生在不同的进程中。

这就是 Node 可以执行异步操作的原因:一个线程正在执行文件系统操作,而主 Node 线程继续执行您的 javascript 代码。在像 Node 这样的事件驱动服务器中,文件系统线程会通知主 Node 线程某些事件,例如完成、失败或进度,以及与该事件相关的任何数据(例如数据库查询的结果或错误消息),主节点线程决定如何处理该数据。

您可以在此处阅读更多相关信息:单线程非阻塞 IO 模型如何在 Node.js 中工作

异步示例是否可以输出与 #1 相同的内容?例如,database.query完成得如此之快,以至于我们到达时console.log任务已经完成。
2021-03-10 14:31:39
简短回答:是的@Abdul,你是对的。长答案:嵌套函数(回调)是按顺序“一个接一个”做事的方式。但这在技术上不是“同步”的。匿名函数仍然在“阻塞操作完成时”执行,或者换句话说,“异步”执行。Node.js 可以在阻塞操作发生时执行其他函数。函数保持异步,只是你在链接它们。同步函数阻止执行,这是关键。
2021-03-11 14:31:39
@JohnGalt sql 在不同的线程上运行。但当然这取决于您使用的 sql 驱动程序的实现。驱动程序应该产生一个新线程,连接到 mysql 并运行查询。完成后,将结果发布到事件队列,Node 将调用回调。
2021-03-16 14:31:39
所以基本上,当我执行的第一段代码,它会做这样的事情: request query.; 5 seconds later when the request is done; console.log; 当第二个执行: request query; console.log; work on the query;
2021-03-17 14:31:39
@TheBronx 如果console.log("Next line");在示例 2 中是在匿名函数内,那么在 之后console.log("query finished");,这意味着“下一行”将在“查询完成”之后打印,对吗?因此,如果我以嵌套方式拥有所有内容,那么所有内容都将以同步方式运行,因此我无需担心使用某些功能的同步版本。我的理解正确吗?
2021-03-19 14:31:39

这两种方法的区别如下:

同步方式: 等待每个操作完成,然后才执行下一个操作。对于您的查询:console.log()除非查询已完成执行以从数据库中获取所有结果,否则命令将不会执行,直到 &。

异步方式: 它从不等待每个操作完成,而是只在第一个 GO 中执行所有操作。一旦结果可用,将处理每个操作的结果。对于您的查询:该console.log()命令将在该Database.Query()方法之后立即执行数据库查询在后台运行并在完成检索数据后加载结果。

用例

  1. 如果您的操作没有像从数据库查询大量数据那样进行繁重的工作,那么请继续使用同步方式,否则使用异步方式。

  2. 在异步方式中,您可以向用户显示一些进度指示器,而在后台您可以继续进行繁重的工作。这是 GUI 应用程序的理想方案。

@Fahmi 理论上是的,实际上是不可能的
2021-03-21 14:31:39
这是否意味着 db.query(cmd, callback) 正在并发运行(如在线程中)?它们是同时运行的吗?
2021-04-04 14:31:39
在他的第二个示例中,是否有可能查询完成得如此之快以至于它首先调用回调,然后再调用console.log
2021-04-09 14:31:39

如果您在两个示例中添加一行,这将变得更加清晰:

var result = database.query("SELECT * FROM hugetable");
console.log(result.length);
console.log("Hello World");

第二个:

database.query("SELECT * FROM hugetable", function(rows) {
   var result = rows;
   console.log(result.length);
});
console.log("Hello World");

尝试运行这些,您会注意到第一个(同步)示例, result.length 将在 'Hello World' 行之前打印出来。在第二个(异步)示例中, result.length 将(很可能)在“Hello World”行之后打印。

这是因为在第二个示例中,database.query是在后台异步运行的,并且脚本会直接继续执行“Hello World”。console.log(result.length)当数据库查询完成时才会执行。

你说: result.length 将(很可能)在“Hello World”行之后打印。.... 为什么那只是“最有可能”?我认为它总是在 console.log 输出之后打印。感谢您的澄清:)
2021-03-15 14:31:39
@humanityANDpeace:这就是异步访问的全部意义所在:你不知道什么时候会完成。也许它是一个非常快的数据库,甚至在 Javascript 到达“Hello World”行之前数据库查询就返回了......
2021-03-21 14:31:39

首先,我意识到我回答这个问题晚了。

在讨论同步和异步之前,让我们简要地看看程序是如何运行的。

同步情况下,每条语句在下一条语句运行之前完成在这种情况下,程序完全按照语句的顺序进行评估。

这就是异步在 JavaScript 中的工作方式。JavaScript 引擎中有两部分,一部分查看代码并将操作排入队列,另一部分处理队列。队列处理发生在一个线程中,这就是为什么一次只能发生一个操作。

当看到异步操作(如第二个数据库查询)时,将解析代码并将操作放入队列,但在这种情况下,会注册回调以在此操作完成时运行。队列中可能已经有许多操作。队列前端的操作被处理并从队列中移除。处理完数据库查询的操作后,将请求发送到数据库,完成后将在完成时执行回调。此时,已“处理”该操作的队列处理器将继续进行下一个操作——在本例中

    console.log("Hello World"); 

数据库查询仍在处理中,但 console.log 操作位于队列的前面并被处理。这是一个同步操作,立即执行,结果立即输出“Hello World”。一段时间后,数据库操作完成,才调用并处理向查询注册的回调,将变量结果的值设置为行。

一个异步操作可能会导致另一个异步操作,这第二个操作将被放入队列中,当它到达队列的前面时,它将被处理。调用异步操作注册的回调是 JavaScript 运行时在操作完成时返回操作结果的方式。

了解哪个 JavaScript 操作是异步的一个简单方法是注意它是否需要回调 - 回调是第一个操作完成时将执行的代码。在题中的两个例子中,我们可以看到只有第二种情况有回调,所以是两者的异步操作。由于处理异步操作的结果的方式不同,情况并非总是如此。

要了解更多信息,请阅读 Promise。Promise 是另一种处理异步操作结果的方式。Promise 的好处在于,它的编码风格更像是同步代码。

许多库(如节点“fs”)为某些操作提供同步和异步样式。在操作不需要很长时间并且不经常使用的情况下 - 如在读取配置文件的情况下 - 同步样式操作将导致代码更易于阅读。

在同步情况下,console.log 命令在 SQL 查询执行完成之前不会执行。

在异步情况下,将直接执行console.log 命令。查询的结果将在之后的某个时间由“回调”函数存储。

但是实际上是同时被调用的吗?让我感到困惑的是,在异步代码中,实际代码是同时并行运行的吗?
2021-03-27 14:31:39
这取决于处理器(它是多核的吗?)和操作系统。参见en.wikipedia.org/wiki/Multithreading_(software)#Multithreading
2021-04-03 14:31:39