Javascript 是单线程的,同一事件只能处理一个任务 Javascript 通过 Event Loop(事件循环)防止主线程阻塞
runtime
Javascript 运行组成
- Javascript Engine。Chrome 的引擎是 V8
- Web APIs。DOM、AJAX、Timeout 都是通过调用 Web APIs 实现的
- Callback Queue,回调队列。即 Web APIs 中的回调函数,都是在回调队列中排队等待运行的
- EventLoop,事件循环。宏任务和微任务的容器
Call Stack
调用栈 调用栈时存放执行的重要条件。因为只有一个调用栈,所以 Javascript 是单线程的
* 追踪函数执行流的机制。当执行环境中调用多个函数时,通过调用栈可以追踪到具体哪一个函数在执行,执行的函数体中又调用了哪些函数
* 每调用一个函数,解释器就会把该函数添加进调用栈并开始执行
* 正在调用栈中执行的函数还调用了其他函数,那么被调用的函数也会被添加进调用栈,一旦这个函数被调用,便会立即执行
* 当前函数执行完毕后,解释器将其清出调用栈,继续执行当前执行环境下的剩余代码
* 当分配的调用栈空间被占满时,会引发“堆栈溢出”
Callback Queue
回调队列
* 在 Javascript 的编译阶段,将一些事件放置在执行队列中
EventLoop
事件循环 Javascript 处理任务是在等待任务、执行任务、休眠等待新任务中不断循环的,这种机制称为事件循环
* 将 Callback Queue 队列中等待执行的事件,放到 Call Stack 中执行
同步任务
微观任务
* 微观任务不属于事件循环,它是 V8 引擎的一个实现,用来实现 Promise 的 then/reject 或其他一些需要同步延后的 callback。本质是哪个
异步任务
异步任务分为 宏任务 和 微任务
宏任务 Macro
参与了事件循环的异步任务
微任务 Micro
没有参与事件循环的”异步”任务 本质:直接在 Javascript 引擎中执行的、没有参与事件循环的任务
* 是一个存在内存回收的任务
* 是一个普通的回调 Callable
* 在当前 Javascript 调用执行完毕后立刻执行的,是同步的
* 微观任务不属于事件循环,它是 V8 引擎的一个实现,用来实现 Promise 的 then/reject 或其他一些需要同步延后的回调
* 本质上它和当前的 V8 调用栈是同步执行的,只是放到了最后面
* 除了 Promise / MutationObserver,在 Javascript 中发起的请求也会创建一个微观任务延后执行
常见异步任务分类
任务 | Chrome | Node | 类别 |
---|---|---|---|
I/O | √ | √ | 宏任务 Marco |
reuqestAnimationFrame | √ | × | |
setTimeout | √ | √ | |
setInterval | √ | √ | |
setImmediate | × | √ | |
process.nextTick | × | √ | 微任务 Micro |
MutationObserver | √ | × | |
Promise | √ | √ |
* requestAnimationFrame 是一个浏览器用于定时循环操作的接口,类似 setTimeout。主要用途是按帧对网页进行重绘
* process.nextTick 是 Node 提供的一个异步执行函数。执行顺序早于 setTimeout 和 setInterval。在主逻辑的末尾任务队列调用之前执行
* MutationObserver 提供了监视 DOM 树变化的能力
优先级
* 同步任务 > 微任务 > 宏任务