JavaScript 既是单线程又是异步的,请问这二者是否冲突,以及有什么区别?

JavaScript 既是单线程又是异步的,请问这二者是否冲突,以及有什么区别?

关注者
298
被浏览
31,331

15 个回答

JS的单线程是指一个浏览器进程中只有一个JS的执行线程,同一时刻内只会有一段代码在执行(你可以使用IE的标签式浏览试试看效果,这时打开的多个页面使用的都是同一个JS执行线程,如果其中一个页面在执行一个运算量较大的function时,其他窗口的JS就会停止工作)。
而异步机制是浏览器的两个或以上常驻线程共同完成的,例如异步请求是由两个常驻线程:JS执行线程和事件触发线程共同完成的,JS的执行线程发起异步请求(这时浏览器会开一条新的HTTP请求线程来执行请求,这时JS的任务已完成,继续执行线程队列中剩下的其他任务),然后在未来的某一时刻事件触发线程监视到之前的发起的HTTP请求已完成,它就会把完成事件插入到JS执行队列的尾部等待JS处理。又例如定时触发(settimeout和setinterval)是由浏览器的定时器线程执行的定时计数,然后在定时时间把定时处理函数的执行请求插入到JS执行队列的尾端(所以用这两个函数的时候,实际的执行时间是大于或等于指定时间的,不保证能准确定时的)。
所以,所谓的JS的单线程和异步更多的应该是属于浏览器的行为,他们之间没有冲突,更不是同一种事物,没有什么区别不区别的。

Javascript 本身是单线程的,并没有异步的特性。

由于 Javascript 的运用场景是浏览器,浏览器本身是典型的 GUI 工作线程,GUI 工作线程在绝大多数系统中都实现为事件处理,避免阻塞交互,因此产生了 Javascript 异步基因。此后种种都源于此。
语言本身是单线程的。同一时刻,有且仅有一处代码正在执行。包括setTime的那两个函数也是如此。
所有涉及到异步的方法和函数都是由浏览器的另一个线程去执行的,程序员所能控制的"另一个线程"也仅限于这些方法。

但凡这种「既是单线程又是异步」的语言有一个共同特点:它们是 event-driven 的。驱动它们的 event 来自一个异构的平台。这些语言的 top-level 不象 C 那样是 main,而是一组 event-handler。虽然所有 event-handler 都在同一个线程内执行,但是它们被调用的时机是由那个驱动平台决定的。而且设计要求每个 event-handler 要尽快结束。未做完的工作可以通知那个异构的驱动平台来完成。所以那个驱动平台可以有许多线程。

(Thread per request 这个说法是错误的,已经有答案反对。其实想想也不可能,JavaScript 根本没有语言级别的 synchronization-primitive,怎么可能支持 TPR。)

本质原因是这一类脚本引擎在实现过程中并没有对应的线程概念,以及引擎能否被多线程安全访问。

如果多个模块在多个线程中运行,并且同时征用脚本引擎来处理一些策略代码时,就产生线程安全问题。要么对虚拟机加锁,要么就是使用事件队列,让虚拟机不停的从事件队列里取事件、执行,将主动权转给虚拟机。通常后一种是比较理想的方式,也是现在的大多数脚本运行环境所提供的。

事件的发生和对事件的处理由于事件队列的存在,之间存在一个等待事件,从而产生一定的时间差——也就是异步了。因此,可以说,乏是使用事件队列来实现的脚本运行环境,通常也都是异步的。

至于你说的单线程,如果整个系统仅有一个线程运行,那将不存在多线程对虚拟机的并发访问,也不需要考虑线程安全了。就不要事件队列,当事件发生时,直接调用虚拟机接口,执行脚本函数,也就不存在异步了。
JS的主进程调用栈是同步的(单线程,一次只能做一件事)
异步调用把回调加入一个队列,等待主栈空闲
JavaScript 既是单线程又是异步的,请问这二者是否冲突,以及有什么区别?主栈空闲,Event触发,Event Loop机制把队列里排在前面的回调压入主栈
JavaScript 既是单线程又是异步的,请问这二者是否冲突,以及有什么区别?
Javascript是单线程
AJAX异步加载是通过浏览器实现的
Javascript执行到相应语句时,向浏览器提交个请求并告诉它回调函数,然后继续执行后面的语句
服务器返回请求后,浏览器调用之前的回调函数(在Js执行完当前不宜中断的语句之后)

一、首先理解一下什么是异步:异步就是把事情交给别人(浏览器)去做,所以自己(js执行线程)不 会阻塞。别人什么时候做什么时候做完我是不关心的。

二、js在浏览器中怎么执行:js执行线程在执行某一js语句的时候会给浏览器发送请求并告知回调函数,然后js执行线程又继续执行其他语句了。等请求完成后浏览器会将回调函数放入执行队列的队尾等待执行。

三、异步是如何实现的:异步是靠浏览器的两个或者两个以上的常驻线程来完成的,第一个就是js的执行线程,第二个就是事件触发线程,js执行线程发出异步请求给浏览器,浏览器开一个新的请求线程来处理,js执行线程继续执行其他任务。那么事件触发线程呢?他就监听这个请求,如果这个请求完成了,就将该事件插入到JS执行队列的尾部等待JS处理。

四、图片能够更加直观的描述。

JavaScript 既是单线程又是异步的,请问这二者是否冲突,以及有什么区别?图一

自己也是学习,有不正确的地方希望看到的同学能够指出来。

简单的词语概念不清才会感觉有冲突

异步相对的,参照物是某个线程。

其概念是,在线程x之外做一个任务a不阻塞线程x让线程x可以做其他的任务b.


这与 语言javascript 具有一个线程x 一点冲突也没有。任务a只要能让系统、运行时底层、远程process 做就可以了。


就算理解不了服务端后台如何做,

利用 xmlhttprequest 做异步请求的例子也应该见过嘛。

Js执行是单线程,但浏览器是多线程
异步的意思其实是把事情丢给别人去做(所以我自己不会阻塞)。别人啥时候做我不关心。
单线程的时候可以用event loop来实现异步。
多线程的话还可以用别的线程去做。

修正一下上面说每个request一个thread的说法。

以FF为例。
实际上,浏览器接受到JS请求的是所谓的主线程。主线程会将此JS事件封装后,指派给专门负责JS处理的线程。处理完后,JS线程同样将请求封装以后投入主线程队列返回。

所有任务可以分成两种,一种是同步任务(synchronous),另一种是异步任务(asynchronous)。同步任务指的是,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;异步任务指的是,不进入主线程、而进入"任务队列"(task queue)的任务,只有"任务队列"通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。

阮一峰大师详解

举个栗子。

绝大部分人,都是单线程,且异步的。

类比一下。

阮一峰大神做过详细的解释,朴灵后来做过批注,详见:JavaScript 运行机制详解:再谈Event Loop