前言
javascript从诞生之日起就是一门单线程的非阻塞的脚本语言。
为什么javascript是单线程,而不是多线程呢?这是由其最初的用途来决定的:与浏览器交互。与浏览器的交互中我们可能需要操作各种dom,试想以下,如果javascript是多线程,当两个线程都需要操作一个dom,例如,一个向其添加节点,一个要删除这个dom,这是浏览器又以哪个为准,所以,因为这些特性,降低浏览器操作的复杂性,javascript就被设计成单线程语言。
单线程就意味着,javascript运行环境在执行代码的时候,都只有一个主线程来处理所有任务,只有前一个任务执行完毕,才能执行后一个任务,所以当有一个比较耗时的任务在执行时,后面的任务不得不等待前一个任务执行完毕,才能执行,这样就造成了很多问题,比如页面卡顿,怎样解决这种问题呢?事件循环机制就是来解决这种问题的。
为了利用多核 CPU 的计算能力,HTML5 提出 Web Worker 标准,允许 JavaScript 脚本创建多个线程,但是子线程完全受主线程控制,且不得操作 DOM。所以,这个新标准并没有改变 JavaScript 单线程的本质。
相关概念
- 进程: 运行的程序就是一个进程,比如你正在运行的浏览器,它会有一个进程。
- 线程: 程序中独立运行的代码段。一个进程 由单个或多个 线程 组成,线程是负责执行代码的。
- 单线程: 从头执行到尾,一行一行执行,如果其中一行代码报错,那么剩下代码将不再执行。同时容易代码阻塞。
- 多线程: 代码运行的环境不同,各线程独立,互不影响,避免阻塞。
javascript事件循环机制图解
上图中,
JS代表的是Javascript引擎在执行代码时产生的内存堆(heap)和执行栈(stack)。
内存堆: 对象被分配在一个堆中,即用以表示一个大部分非结构化的内存区域。
执行栈: 执行同步代码的地方。Web APIS代表的是浏览器提供的Api。(JSApi不仅仅是JS引擎提供,还有浏览器提供,比如:DOM、AJAX、Timeout等,一般异步的API都是浏览器提供的)
Callback Queue代表的是事件队列,当WebAPIS执行完一项异步任务,就会将相应的回调事件放在此队列中。
Event Loop代表的是主线程从"事件队列"中读取事件,这个过程是循环不断的,所以整个的这种运行机制被称为Event Loop(事件循环)。
了解完上图中的含义我们现在就可以把Javascript事件循环机制大致的流程梳理一遍了:
1、Javascript引擎在执行代码时产生的内存堆(heap)和执行栈(stack)
2、在代码执行过程中会有一些异步任务,这些异步任务就会交给WebAPIs来处理,执行栈接着执行下面的同步代码
3、WebAPIs处理完这些异步任务之后会把相应的回调事件放入事件队列中
4、当执行栈中的代码执行完毕,主线程就会从事件队列中读取事件,然后再在主线程中执行
5、就一直循环步骤4,这样就构成了事件循环机制