event-loop
本文大部分概念参照JavaScript 运行机制详解:再谈Event Loop,并稍加理解,主要是为了记忆和理解相关概念。
什么是event-loop
首先我们都知道javvascript 是单线程的,那也就是所同一事件只能做一件事。那么为什么JavaScript是单线程的呢?
为什么JavaScript是单线程?
JavaScript的单线程,与它的用途有关。作为浏览器脚本语言,JavaScript的主要用途是与用户互动,以及操作DOM。这决定了它只能是单线程,否则会带来很复杂的同步问题。比如,假定JavaScript同时有两个线程,一个线程在某个DOM节点上添加内容,另一个线程删除了这个节点,这时浏览器应该以哪个线程为准?
所以,为了避免复杂性,从一诞生,JavaScript就是单线程,这已经成了这门语言的核心特征,将来也不会改变。
为了利用多核CPU的计算能力,HTML5提出Web Worker标准,允许JavaScript脚本创建多个线程,但是子线程完全受主线程控制,且不得操作DOM。所以,这个新标准并没有改变JavaScript单线程的本质。
同步任务和异步任务
由于单线程的特性,所有的任务都需要排队。不论前一个任务耗时多久后一个任务都需要等待直到前一个任务结束。
但假如前一个任务是I/O或者Ajax,受限于外部原因导致任务耗时很久就会导致进程阻塞,而此时cpu是空闲的完全可以不管I/O或者Ajax挂起等待中的任务,先运行排在后面的任务等到I/O或者Ajax返回了结果在回过头把挂起的任务继续下去。
于是所有的任务可以分成两种同步任务和异步任务
- 同步任务:在
主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务 - 异步任务:不进入主线程、而进入
任务队列(task queue)的任务,只有任务队列通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行
所以javascript的事件运行机制如下:
- 所有同步任务都在
主线程上执行,形成一个执行栈(execution context stack) 主线程之外,还存在一个任务队列(task queue)。只要异步任务有了运行结果,就在任务队列之中放置一个事件- 一旦
执行栈中的所有同步任务执行完毕,系统就会读取任务队列,看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行 - 主线程不断重复上面的第三步
主线程从任务队列中读取事件,这个过程是循环不断的,所以整个的这种运行机制又称为Event Loop(事件循环)
什么是宏任务?什么是微任务
概念
-
宏任务:当前调用栈中执行的代码成为
宏任务。(主代码快,定时器等等)。 -
微任务:当前(此次事件循环中)
宏任务执行完,在下一个宏任务开始之前需要执行的任务,可以理解为回调事件。(promise.then,proness.nextTick等等)。 -
宏任务中的事件放在callback queue中,由事件触发线程维护;微任务的事件放在微任务队列中,由js引擎线程维护
执行机制
主任务(宏任务) ---> 所有微任务 ---> 宏任务
宏任务与微任务具体包含哪些方法
宏任务
| # | 浏览器 | Node |
|---|---|---|
I/O | ✅ | ✅ |
setTimeout | ✅ | ✅ |
setInterval | ✅ | ✅ |
setImmediate | ❌ | ✅ |
requestAnimationFrame | ✅ | ❌ |
微任务
| # | 浏览器 | Node |
|---|---|---|
process.nextTick | ❌ | ✅ |
MutationObserver | ✅ | ❌ |
Promise.then catch finally | ✅ | ✅ |