javascript如何单线程和异步

时间:2018-06-24 06:55:00

标签: javascript

我通过下面的链接,对单线程javascript及其异步性质有所了解

https://www.sohamkamani.com/blog/2016/03/14/wrapping-your-head-around-async-programming/

但是我仍然有疑问,javascript是单线程的,并且它总是以顺序的方式向前移动,直到完成执行。

每当我们调用具有回调的函数时,该回调将在函数收到响应后执行。在等待响应期间,将继续执行javascript代码。这样,在顺序执行的情况下,如何在收到响应后立即恢复回调执行。就像线程正在向后移动以执行回调。

执行线程应该始终向前移动吗?

请对此进行澄清。

2 个答案:

答案 0 :(得分:14)

JavaScript(该语言)不是单线程的,并且存在运行JavaScript的多线程环境(例如,通过其脚本支持的Java虚拟机)。语言本身在线程主题上基本没有任何意见。它确实定义了一个工作队列(面向浏览器的人称其为“事件循环”)和工作的运行至完成语义(请参见规范中的Jobs and Job Queues)。稍后再讨论。

但是,大多数环境(包括浏览器)在每个全局环境中运行一个线程(或者有时在多个全局环境中运行一个线程),这就是为什么人们普遍认为“ JavaScript是单线程”的原因。但是即使在浏览器上,您也可以通过Web Worker拥有多个线程。他们并没有一个共同的全球环境,它带来了所有复杂的原因,但是他们可以交流。

回到您的问题:

在单个线程上运行并具有异步回调完全没有冲突。 JavaScript线程基于添加了作业的作业队列起作用。作业是运行至完成的代码单元。当该代码单元完成运行直至完成时,线程将从队列中拾取下一个作业并运行该作业。一个作业不能中断另一个作业(spec link)。尽管可以在工作线程上进行作业(通过Atomics.wait),但在主UI线程上运行的作业不能在中间(大部分为¹)暂停。具有已暂停作业的线程已完全暂停,在恢复并完成已暂停的作业之前,不会从队列中拾取其他作业。

例如,考虑:

console.log("one");
setTimeout(function() {
  console.log("three");
}, 10);
console.log("two");

运行该命令时,您会看到

one
two
three
在控制台中

。这是发生了什么:

  1. 将用于执行主脚本的作业添加到作业队列中
  2. 浏览器的主要JavaScript线程完成了这项工作
  3. 它运行了第一个console.logsetTimeout和最后一个console.log
  4. 工作终止
  5. JavaScript主线程闲置了一段时间
  6. 浏览器的计时器机制确定该setTimeout回调的时间到了,并向作业队列中添加了一个作业以运行它
  7. 主要的JavaScript线程完成了该工作并运行了最终的console.log

如果主JavaScript线程被捆绑在一起(例如while (true);),则作业只会堆积在队列中,并且永远不会得到处理,因为该作业永远不会完成。


¹“工作是运行到完成的代码单元。” “工作不能在中间暂停...” 这里有两个警告:

  1. alertconfirmprompt-那些90年代的同步用户交互-在等待用户时暂停主UI线程上的作业。这是过时的行为(在其中至少部分是phased out)。

  2. 自然地,宿主进程(浏览器等)可以在作业运行时终止正在运行的整个环境。例如,当网页变为“无响应”时,浏览器可以将其杀死。但这不只是工作,而是工作运行的整个环境。

答案 1 :(得分:1)

只需添加到T.J. Crowder的上述答复中即可:

作业队列被称为事件循环,它跟踪所有需要执行的回调。每当准备好执行回调时(例如:异步操作完成后),便会将其添加到事件循环中。

如T.J.拥挤者,您可以将事件循环想象为一个队列。每当循环中有要执行的回调时,循环就会控制主线程并执行该回调。发生这种情况时,正常流程的执行将停止。这样,可以将JavaScript想象成一种单线程语言。

您可以在Philip Roberts的amazing talk中了解有关事件循环及其工作原理的更多信息。