JavaScript单线程运行机制与并发模型

一、为什么JavaScript是单线程?

JavaScript语言的一大特点就是单线程,也就是说,同一时间只能做一件事。具体地,一个window对应一个JavaScript线程。

JavaScript的单线程,与它的用途有关。作为浏览器脚本语言,JavaScript的主要用途是与用户互动,以及操作DOM。这决定了它只能是单线程,否则会带来很复杂的同步问题。比如,假定JavaScript同时有两个线程,一个线程在某个DOM节点上添加内容,另一个线程删除了这个节点,这时浏览器应该以哪个线程为准?

所以,为了避免复杂性,从一诞生,JavaScript就是单线程,这已经成了这门语言的核心特征,将来也不会改变。

为了利用多核CPU的计算能力,HTML5提出Web Worker标准,允许JavaScript脚本创建多个线程,但是子线程完全受主线程控制,且不得操作DOM。所以,这个新标准并没有改变JavaScript单线程的本质。

二、JavaScript并发模型与事件循环

单线程就意味着,所有任务需要排队,前一个任务结束,才会执行后一个任务。那么JavaScript是如何实现异步调用的呢?

JavaScript里这种并发的一种理论上的模型如下:

JavaScript单线程运行机制与并发模型_第1张图片

(1)所有同步任务依次被执行,在内存栈区域产生相应的栈帧(活动记录)。

(2)一个JavaScript运行时包含一个待处理的消息队列,每个消息都与一个函数相关联。当栈为空时,就从队列中取出一个消息并进行处理。这个处理过程包含了调用与这个消息相关联的函数(以及因而创建了一个初始堆栈帧)。当栈再次为空的时候,也就意味着消息处理结束。

这种处理方式通常使用如下的方式来实现:

while(queue.waitForMessage()){
    queue.processNextMessage();
}

  如果当前没有任何消息,queue.waitForMessage会同步等待消息到来。

因此这个过程被称为事件循环。

每一个消息执行完成后,其他消息才会被执行。在浏览器里,当一个事件出现且一个事件监听程序被绑定时,消息会被随时添加,如果没有事件监听器,则事件丢失。调用setTimeout函数会在一个时间段过去后在队列中添加一个消息,这个时间段作为函数的第二个参数被传入。如果队列中没有其他消息,那么这个消息会被马上处理,但是如果有其他消息,则setTimeout消息必须等待其他消息处理完。因此第二个参数仅仅表示最少的时间而非确切的时间。

以Ajax为例,事件循环模型如下:

JavaScript单线程运行机制与并发模型_第2张图片

 

你可能感兴趣的:(javascript)