我通过下面的链接,对单线程javascript及其异步性质有所了解
https://www.sohamkamani.com/blog/2016/03/14/wrapping-your-head-around-async-programming/
但是我仍然有疑问,javascript是单线程的,并且它总是以顺序的方式向前移动,直到完成执行。
每当我们调用具有回调的函数时,该回调将在函数收到响应后执行。在等待响应期间,将继续执行javascript代码。这样,在顺序执行的情况下,如何在收到响应后立即恢复回调执行。就像线程正在向后移动以执行回调。
执行线程应该始终向前移动吗?
请对此进行澄清。
答案 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在控制台中
。这是发生了什么:
console.log
,setTimeout
和最后一个console.log
setTimeout
回调的时间到了,并向作业队列中添加了一个作业以运行它console.log
如果主JavaScript线程被捆绑在一起(例如while (true);
),则作业只会堆积在队列中,并且永远不会得到处理,因为该作业永远不会完成。
¹“工作是运行到完成的代码单元。” 和“工作不能在中间暂停...” 这里有两个警告:
alert
,confirm
和prompt
-那些90年代的同步用户交互-在等待用户时暂停主UI线程上的作业。这是过时的行为(在其中至少部分是phased out)。
自然地,宿主进程(浏览器等)可以在作业运行时终止正在运行的整个环境。例如,当网页变为“无响应”时,浏览器可以将其杀死。但这不只是工作,而是工作运行的整个环境。
答案 1 :(得分:1)
只需添加到T.J. Crowder的上述答复中即可:
作业队列被称为事件循环,它跟踪所有需要执行的回调。每当准备好执行回调时(例如:异步操作完成后),便会将其添加到事件循环中。
如T.J.拥挤者,您可以将事件循环想象为一个队列。每当循环中有要执行的回调时,循环就会控制主线程并执行该回调。发生这种情况时,正常流程的执行将停止。这样,可以将JavaScript想象成一种单线程语言。
您可以在Philip Roberts的amazing talk中了解有关事件循环及其工作原理的更多信息。