在了解 JavaScript 单线程、非阻塞机制之前,先了解几组易混淆的概念。
进程和线程的概念以及关系:
并行和并发的概念:
阻塞和非阻塞的概念:
再来区分单线程和多线程的区别:
同步与异步的概念:
JavaScript 的运行通常是在浏览器环境中进行的,具体由 JavaScript 引擎去解析和运行。
目前最为流行的浏览器为:Chrome、IE、Safari、Firefox、Opera。浏览器的内核是多线程的,通常由以下几个常驻的线程组成:
setTimeout
、setInterval
⚠️ 需要注意的是,渲染线程和 JavaScript 引擎线程是 互斥 的。渲染线程在执行任务的时候,JavaScript 引擎线程会被挂起。因为 JavaScript 可以操作 DOM,若在渲染中 JavaScript 处理了 DOM,浏览器可能会不知所措了。
通常讲到浏览器的时候,我们会说到两个浏览器的核心组件:渲染引擎(Rendering Engine)和 JavaScript 解释器(JavaScript Interpreter)。
浏览器厂商 | 渲染引擎 | JavaScript 解释器(引擎) |
---|---|---|
Chrome | Webkit => Blink | V8 |
Safari | Webkit | Nitro |
Firefox | Gecko | SpiderMonky / TraceMonkey / JaegerMonkey |
Opera | Presto => Blink | Linear A / Linear B / Futhark / Carakan |
Internet Explorer | Trident => EdgeHTML | JScript / Chakra(9+) |
Edge | EdgeHTML => Chromium | Chakra |
注:Webkit 引擎包含 WebCore 排版引擎及 JavaScript Core 解析引擎
不同的渲染引擎对同一个样式的实现不一致,就导致了经常被人诟病的浏览器样式兼容性问题。
JavaScript 解释器可以说是 JavaScript 虚拟机,负责 JavaScript 代码的解析和执行。这里 编译阶段 有详细解读。
JavaScript 的单线程,与它的用途有关。作为浏览器脚本语言,JavaScript 的主要用途是与用户互动,以及操作 DOM。这决定了它只能是单线程,否则会带来很复杂的同步问题。比如,假定 JavaScript 同时有两个线程,一个线程在某个 DOM 节点上添加内容,另一个线程删除了这个节点,这时浏览器应该以哪个线程为准呢?
所以,为了避免复杂性,从诞生之初以来,JavaScript 运行环境就是单线程,这已经成了这门语言的核心特征,将来也不会改变。
为了利用多核 CPU 的计算能力,HTML5 提出 Web Worker 标准,允许 JavaScript 脚本创建多个线程,但是子线程完全受主线程控制,且不得操作 DOM。所以,这个新标准并没有改变 JavaScript 单线程的本质。
⚠️ 注意: 需要注意的是,JavaScript 的单线程是指一个程序进程(在浏览器运行环境中运行的就是浏览器进程)中只有一个 JavaScript 的执行线程,同一时刻内只会有一段 JavaScript 代码在执行。而异步机制是运行环境的两个或以上常驻线程共同完成的。
JavaScript 中的程序任务可以分为两种:
具体来说,异步执行的运行机制 如下: