当JavaScript是单线程语言时,如何在JavaScript中使用异步无阻塞代码?

当JavaScript是单线程语言时,如何在JavaScript中使用异步无阻塞代码?,javascript,asynchronous,async-await,es6-promise,Javascript,Asynchronous,Async Await,Es6 Promise,我是JavaScript新手,我习惯于通过创建工作线程在Java中创建异步无阻塞代码 我不明白异步无阻塞代码在JavaScript中是如何工作的,因为JavaScript是一种单线程语言 例如,JavaScript中的承诺和回调 两者都是无阻塞的,并且允许主线程继续逐行执行程序的其余部分,并且仅当承诺在稍后时间(例如:数据准备就绪)得到满足时,才会执行promise.resolve()或回调 现在,我很难理解,如果主线程已经移动,并且正在忙于做不同的事情,那么哪个线程确切地跟踪承诺何时实现/准备

我是JavaScript新手,我习惯于通过创建工作线程在Java中创建异步无阻塞代码

我不明白异步无阻塞代码在JavaScript中是如何工作的,因为JavaScript是一种单线程语言

例如,JavaScript中的承诺回调

两者都是无阻塞的,并且允许主线程继续逐行执行程序的其余部分,并且仅当承诺在稍后时间(例如:数据准备就绪)得到满足时,才会执行promise.resolve()或回调

现在,我很难理解,如果主线程已经移动,并且正在忙于做不同的事情,那么哪个线程确切地跟踪承诺何时实现/准备就绪,或者回调何时准备好执行

作为一名Java程序员,我的逻辑告诉我,必须有一个后台工作线程,负责在回调/承诺准备好执行时通知主线程,这与JavaScript是单线程的事实相矛盾,所以我一定错了

我想对这个概念有一个很好的解释。
提前谢谢

使用计算机实现并发行为有两种方法:

  • 同时运行两台计算机(或处理器)(多线程)

  • 您可以非常快速地在不同的任务之间切换,这样看起来好像它们会同时运行(多任务)

  • 现在,虽然Java经常在这些任务之间切换,但JavaScript只在当前任务完成时切换任务;因此,代码不会并发运行,并且表现为“单线程”

    JavaScript以“单线程方式”执行并不意味着底层引擎是单线程的。它不是,它使用一些线程来管理I/O。无论何时启动异步操作,引擎都会将其委托给一个后台线程;然后,当任务完成时,另一个线程将结果返回给主线程,然后主线程将调用JavaScript代码


    您可以将JavaScript引擎看作是一个
    任务执行器
    ,它在
    线程池
    中启动线程。但是,您只控制
    任务执行器

    承诺/回调被阻止
    。您可以运行以下示例:

    function func1() {
      return new Promise(resolve => {
        setTimeout(() => {
          console.log('func1');
          while (true) {}
          resolve();
        }, 0);
      });
    }
    
    function func2() {
      func1();
      func1();
      console.log('func2');
    }
    
    func2();
    
    您将看到
    func1
    只打印一次,因为第一个执行func1()的承诺会阻止第二个被调用

    您可以考虑将每个代码块放置在事件循环的队列中。

    对于异步调用,代码的异步部分已排队,等待事件循环运行。当然,事件循环本身是JavaScript应用程序中的单个线程

    例如,如果将while循环移动到
    func2()
    的末尾,将永远看不到打印出
    func1
    ,因为函数func2()正在阻塞队列,因此承诺将永远无法解析


    充当调度程序的“线程”应该是应用程序之外的另一个线程,但由于它不是应用程序的一部分(由JavaScript开发人员/用户编写),我不认为JavaScript不是“单线程语言”。

    它基于事件。运行时等待事件发生,并在事件发生时调用事件处理程序。构建在JavaScript之上的运行时(如WebAPI和node.js)是多线程的,并提供异步特性。像
    setTimeout
    这样的东西不是JavaScript的一部分,而是运行时。您应该研究事件循环及其工作原理。请注意,您的两个示例是错误的,承诺和回调是阻塞的。立即解析的承诺只会被推到当前执行任务的末尾,但如果继续在循环中直接解析承诺,则会阻塞整个线程。调用回调的任务可以是异步的,而不是回调本身;但是这个任务也很可能不是用javascript编写的;-)非常感谢大家的宝贵意见,非常感谢!非常感谢您的澄清,非常好的示例代码!非常感谢!非常感谢。很好的解释!