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


189

我一直在阅读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')

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

这两部分之间的区别对我来说很模糊。输出是什么?

搜寻异步编程也没有帮助我。


41
坦tang,您在Google上找不到任何东西,这是一个相当大的主题。在同步编程中,每个步骤在上一个步骤执行完毕后执行一个步骤。异步地,即使步骤1尚未完成,也将执行步骤2。您在第二个示例中看到的函数称为callBack函数,它将在返回数据库结果后立即运行,这可能是在console.log运行之后。
Laurent S.

7
@Bartdude上有很多关于异步编程的内容,但是对于它的含义以及在实践中的含义并没有多少简单的解释。
Azeirah 2013年

1
@GabrielLlamas为什么要避免使用同步功能?
查理·帕克

3
@CharlieParker因为它们阻塞了事件循环,所以您将失去异步事件I / O模型的所有好处。而且因为这是一个不好的做法。这样考虑:如果您不使用异步函数,为什么要使用Node.js?
加布里埃尔·拉马斯

1
@GabrielLlamas,如果我正在执行INSERT查询,并且我想使用后面的最后插入的ID database.query(),那么我应该以同步方式调用它,对吗?还是应该采取什么方法?(这个问题我有很长一段时间)

Answers:


224

区别在于,在第一个示例中,程序将在第一行中阻塞。下一行(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代码。在节点等事件驱动的服务器中,文件系统线程将某些事件(例如完成,失败或进度)以及与该事件相关的任何数据(例如数据库查询或错误的结果)通知主节点线程。消息),然后主节点线程决定如何处理该数据。

您可以在此处了解更多信息:单线程无阻塞IO模型如何在Node.js中工作


9
所以基本上,当我执行的第一段代码,它会做这样的事情: request query.; 5 seconds later when the request is done; console.log; 当第二个执行: request query; console.log; work on the query;
Azeirah

1
@JohnGalt sql在不同的线程上运行。但是,当然这取决于您使用的sql驱动程序的实现。驱动程序应产生一个新线程,连接到mysql并运行查询。完成后,将结果发布到事件队列中,然后Node将调用回调。
Salvatorelab

4
异步示例是否有可能输出与#1相同的内容?例如,database.query完成如此之快,以至于我们console.log完成任务时就已经完成了。
greatwolf

2
@TheBronx如果console.log("Next line");在示例2中位于匿名函数内部,那么在之后console.log("query finished");,这意味着在“查询完成”之后打印“下一行”对吗?因此,如果我以嵌套方式拥有所有内容,那么所有内容都将以同步方式运行,因此,我不必担心使用某些功能的同步版本。我的理解正确吗?
阿卜杜勒

4
简短答案:是@Abdul,您是对的。长答案:嵌套函数(回调)是按顺序执行事情的方法,“一个接一个”。但这在技术上不是“同步的”。匿名功能仍在“阻止操作完成时”执行,换句话说,“异步”执行。发生阻塞操作时,Node.js可以执行其他功能。功能保持异步,只是将它们链接在一起。同步功能会阻止执行,这就是关键。
Salvatorelab'3

74

这两种方法之间的区别如下:

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

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

用例

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

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


2
这是否意味着db.query(cmd,callback)同时运行(如在线程中)?他们在同时运行吗?
查理·帕克

在他的第二个示例中,是否有可能查询完成得如此之快,以至于在查询之前先调用了回调函数console.log

@Fahmi理论上是,实际上几乎是不可能的
Leo Messi

24

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

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”行之前打印出来。在第二个(异步)示例中,(最有可能)在“ Hello World”行之后打印result.length。

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


1
您说:result.length将(最有可能)在“ Hello World”行之后打印。....为什么那只是“最有可能”?我认为它总是在console.log输出之后打印。感谢您的澄清:)
humanityANDpeace 2014年

9
@humanityANDpeace:这就是异步访问的全部要点:您不知道何时完成。也许这是一个荒谬的快速数据库,并且甚至在Javascript到达“ Hello World”行之前,数据库查询都会返回……
Martijn 2014年

19

首先,我意识到我在回答这个问题上很晚。

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

同步情况下,每个语句在下一个语句运行之前完成。在这种情况下,将按照语句的顺序准确地评估程序。

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

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

    console.log("Hello World"); 

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

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

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

要了解更多信息,请阅读诺言。承诺是可以处理异步操作结果的另一种方法。promise的好处是编码风格更像是同步代码。

许多库(例如节点“ fs”)为某些操作提供同步和异步样式。如果操作时间不长且不常用-例如读取配置文件-同步样式操作将使代码更易于阅读。


6

在同步情况下,直到SQL查询执行完毕,才会执行console.log命令。

在异步情况下,将直接执行console.log命令。之后,查询结果将通过“回调”功能存储。


1
但是实际上是同时被调用吗?令我困惑的是,在异步代码中,实际的代码是同时并行运行的吗?
查理·帕克

这取决于处理器(是否为多核?)和操作系统。见en.wikipedia.org/wiki/Multithreading_(software)#Multithreading
有关

4

主要区别在于异步编程,否则您不会停止执行。您可以在执行“请求”时继续执行其他代码。


2

该函数使第二个异步。

第一个强制程序在下一行可以继续之前,等待程序的每一行完成运行。第二条允许每条线同时(独立)运行。

允许异步或并发的语言和框架(js,node.js)非常适合需要实时传输的事物(例如,聊天,股票应用程序)。


0

同步编程

诸如C,C#,Java之类的编程语言都是同步编程,因此无论您编写什么,都将按照编写顺序执行。

-GET DATA FROM SQL.
//Suppose fetching data take 500 msec

-PERFORM SOME OTHER FUNCTION.
//Performing some function other will take 100 msec, but execution of other 
//task start only when fetching of sql data done (i.e some other function 
//can execute only after first in process job finishes).

-TOTAL TIME OF EXECUTION IS ALWAYS GREATER THAN (500 + 100 + processing time) 
msec

异步

NodeJ具有异步功能,它本质上是非阻塞的,假设在任何需要花费时间(读取,写入,读取)的I / O任务中,nodejs不会保持空闲状态并等待任务完成,将开始执行队列中的下一个任务,并且每当完成任务时,它将使用回调通知。以下示例将有所帮助:

//Nodejs uses callback pattern to describe functions.
//Please read callback pattern to understand this example

//Suppose following function (I/O involved) took 500 msec
function timeConsumingFunction(params, callback){
  //GET DATA FROM SQL
  getDataFromSql(params, function(error, results){
    if(error){
      callback(error);
    }
    else{
      callback(null, results);
    }
  })
}

//Suppose following function is non-blocking and took 100 msec
function someOtherTask(){
  //some other task
  console.log('Some Task 1');
  console.log('Some Task 2');
}

console.log('Execution Start');

//Start With this function
timeConsumingFunction(params, function(error, results){
    if(error){
      console.log('Error')
    }
    else{
      console.log('Successfull'); 
    }
  })

//As (suppose) timeConsumingFunction took 500 msec, 
//As NodeJs is non-blocking, rather than remain idle for 500 msec, it will start 
//execute following function immediately
someOtherTask();

简而言之,输出为:

Execution Start
//Roughly after 105 msec (5 msec it'll take in processing)
Some Task 1
Some Task 2
//Roughly After 510 msec
Error/Successful //depends on success and failure of DB function execution

明显的区别是同步肯定要花费超过600(500 + 100 +处理时间)毫秒,异步可以节省时间。


0

同步功能正在阻塞,而异步功能没有。在同步函数中,语句在下一条语句运行之前完成。在这种情况下,如果按照其中一条语句花费的时间很长,则会按语句的顺序准确地评估程序,并暂停执行程序。

异步函数通常接受回调作为参数,并且在调用异步函数后立即在下一行继续执行。仅当异步操作完成并且调用堆栈为空时才调用回调。繁重的操作(例如从Web服务器加载数据或查询数据库)应异步进行,以便主线程可以继续执行其他操作,而不会阻塞直到完成该长的操作(对于浏览器,UI会冻结) 。

原始发布于Github:链接


0

JS中的异步编程:

同步

  • 停止执行其他代码,直到完成。
  • 由于它停止了进一步执行,因此同步代码称为“阻塞”。在没有其他代码将被执行的意义上进行阻塞。

异步

  • 它的执行被推迟到事件循环中,这是JS虚拟机中的一个构造,该构造执行异步功能(在同步功能栈为空之后)。
  • 异步代码称为非阻止,因为它不会阻止其他代码运行。

例:

// This function is synchronous
function log(arg) {
    console.log(arg)
}

log(1);

// This function is asynchronous
setTimeout(() => {
    console.log(2)
}, 0);

log(3)

  • 该示例记录1、3、2。
  • 2被最后记录,因为它位于一个异步函数中,该函数在堆栈为空后执行。
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.