Node.js 单线程同步和异步混淆

Node.js 单线程同步和异步混淆,node.js,asynchronous,Node.js,Asynchronous,假设makeBurger()需要10秒 在同步程序中 function serveBurger() { makeBurger(); makeBurger(); console.log("READY") // Assume takes 5 seconds to log. } 执行此操作总共需要25秒 因此,对于NodeJs,假设我们制作了一个异步版本的makeBurgerAsync(),这也需要10秒 function serveBurger() { makeBurge

假设
makeBurger()
需要10秒

在同步程序中

function serveBurger() {
   makeBurger();
   makeBurger();
   console.log("READY") // Assume takes 5 seconds to log. 
}
执行此操作总共需要25秒

因此,对于NodeJs,假设我们制作了一个异步版本的
makeBurgerAsync()
,这也需要10秒

function serveBurger() {
   makeBurgerAsync(function(count) {

   });
   makeBurgerAsync(function(count) {

   });  
   console.log("READY") // Assume takes 5 seconds to log. 
}
因为它是单线程。我无法想象幕后到底发生了什么

  • 因此,当函数运行时,两个异步函数都将进入事件循环,并且
    console.log(“READY”)
    将立即执行
  • 但是当执行
    console.log(“READY”)
    时,两个异步函数都没有真正完成工作,对吗?因为单线程占用console.log 5秒钟
  • 完成console.log之后。CPU将有时间在两个异步之间切换,以便每次都可以运行一点每个函数
  • 因此,根据这一点,函数不一定会导致更快的执行,异步可能会因为在事件循环之间切换而变得更慢?我想,在一天结束的时候,所有的东西都会分散在一个线程上,这和同步版本是一样的吗

    我可能错过了一些非常大的概念,所以请让我知道。谢谢

    编辑 如果异步操作类似于查询数据库等,这是有意义的。基本上nodejs只会说“heydb帮我处理这个,而我会做其他事情”。然而,我不理解的情况是nodejs本身中的自定义回调函数

    EDIT2

    function makeBurger() {
        var count = 0;
        count++; // 1 time
        ...
        count++; // 999999 times
        return count;
    }
    
    function makeBurgerAsync(callback) {
        var count = 0;
        count++; // 1 time
        ...
        count++; // 999999 times
        callback(count);
    }
    

    在node.js中,所有异步操作都在node.js Javascript单线程之外完成任务。它们要么使用本机代码线程(如node.js中的磁盘I/O),要么根本不使用线程(如事件驱动网络或计时器)

    您不能完全采用node.js Javascript编写的同步操作,而神奇地使其异步。异步操作是异步的,因为它调用一些用本机代码实现并以实际异步方式编写的函数。因此,要使某些东西异步,必须专门编写以使用低级别操作,这些操作本身与异步本机代码实现是异步的

    这些带外操作,然后通过事件队列与主node.js Javascript线程通信。当其中一个异步操作完成时,它会将一个事件添加到Javascript事件队列中,然后当单node.js线程完成当前正在执行的操作时,它会从事件队列中获取下一个事件并调用与该事件关联的回调

    因此,可以并行运行多个异步操作。并行运行3个操作通常比顺序运行相同的3个操作具有更短的端到端运行时间

    让我们检查一个真实的异步情况,而不是您的伪代码:

    function doSomething() {
       fs.readFile(fname, function(err, data) {
           console.log("file read");
       });
       setTimeout(function() {
           console.log("timer fired");
       }, 100);
    
       http.get(someUrl, function(err, response, body) {
           console.log("http get finished");
       });
    
       console.log("READY");
    }
    
    doSomething();
    
    console.log("AFTER");
    
    下面是逐步发生的情况:

  • fs.readFile()
    已启动。由于node.js使用线程池实现文件I/O,因此此操作将传递给node.js中的一个线程,并将在单独的线程中运行
  • 无需等待
    fs.readFile()
    完成,即可调用
    setTimeout()
    。这使用libuv(node.js构建的跨平台库)中的计时器子系统。这也是非阻塞的,因此计时器被注册,然后继续执行
  • 调用http.get()。这将发送所需的http请求,然后立即返回到进一步执行
  • console.log(“就绪”)
    将运行
  • 这三个异步操作将以不确定的顺序完成(先完成操作的将首先完成)。在本讨论中,假设
    setTimeout()
    首先完成。完成后,node.js中的一些内部构件将在事件队列中插入一个事件,其中包含计时器事件和已注册的回调。当node.js主js线程执行完任何其他js后,它将从事件队列中抓取下一个事件,并调用与其关联的回调
  • 在本说明中,假设在执行计时器回调时,
    fs.readFile()
    操作完成。使用自己的线程,它将在node.js事件队列中插入一个事件
  • 现在,
    setTimeout()
    回调完成。此时,JS解释器将检查事件队列中是否有其他事件。
    fs.readfile()
    事件位于队列中,因此它将获取该事件并调用与该事件关联的回调。该回调执行并完成
  • 一段时间后,
    http.get()
    操作完成。在node.js内部,事件被添加到事件队列中。由于事件队列中没有其他内容,并且JS解释器当前没有执行,因此可以立即为该事件提供服务,并且可以调用
    http.get()
    的回调
  • 根据上述事件序列,您将在控制台中看到:

    READY
    AFTER
    timer fired
    file read
    http get finished
    
    请记住,这里最后三行的顺序是不确定的(它只是基于不可预测的执行速度),因此这里的精确顺序只是一个示例。如果您需要按照特定的顺序执行这些代码,或者需要知道这三个代码是何时完成的,那么您必须添加额外的代码来跟踪这些代码


    既然您试图通过使某些当前不异步的东西异步化来加快代码的运行速度,那么让我重复一遍。您不能完全使用Javascript编写同步操作并“使其异步”。您必须从头开始重写它,以使用根本不同的异步低级操作,或者您必须将其传递给其他进程执行,然后在执行时收到通知