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

时间:2013-05-02 11:00:47

标签: javascript node.js asynchronous synchronous

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

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

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

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

10 个答案:

答案 0 :(得分:205)

区别在于第一个例子,程序将在第一行中阻止。下一行(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
  3. 注意
    虽然Node本身是单线程,但有些任务可以并行运行。例如,文件系统操作发生在不同的进程中。

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

    您可以在此处详细了解:How the single threaded non blocking IO model works in Node.js

答案 1 :(得分:69)

这两种方法的区别如下:

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

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

使用案例

  1. 如果你的操作没有像从DB那里查询大量数据那么繁重,那么继续使用Synchronous方式,否则采用异步方式。

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

答案 2 :(得分:22)

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

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)仅在数据库查询完成时执行。

答案 3 :(得分:18)

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

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

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

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

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

    console.log("Hello World"); 

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

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

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

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

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

答案 4 :(得分:5)

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

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

答案 5 :(得分:4)

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

答案 6 :(得分:2)

该函数使第二个异步。

第一个强制程序等待每一行完成它,然后才能继续下一个。第二个允许每一行一起(并独立)运行。

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

答案 7 :(得分:1)

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被最后记录,因为它位于一个异步函数中,该函数在堆栈为空后执行。

答案 8 :(得分: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

<强>异步

NodeJs提出异步功能,它本质上是非阻塞的,假设在任何需要时间(读取,写入,读取)的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 +处理时间)毫秒,异步可以节省时间。

答案 9 :(得分:0)

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

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

原始文章发布在Github上:Link