一、基本概念
Event Loop
Javascript
有一个 main thread
主线程和 call-stack
调用栈(执行栈),所有的任务都会被放到调用栈等待主线程执行。
任务队列(task queue)
task
,就是任务的意思,我们这里理解为每一个语句就是一个任务
console.log(1);
console.log(2);
如上语句,其实就是就可以理解为两个 task
。
而 queue
呢,就是FIFO
(first in first out 先进先出
)的队列!
所以 Task Queue
就是承载任务的队列。而 JavaScript
的 Event Loop
就是会不断地过来找这个 queue
,问有没有 task
可以运行运行。
同步任务(SyncTask)、异步任务(AsyncTask)
先同步任务 后异步任务
同步任务
- 基本上所有非微任务宏任务的 script
-
异步任务
promise.then 中的函数是异步执行的。
- 所有的宏任务(MacroTask)、微任务(MicroTask)
例子
```javascript const promise = new Promise((resolve, reject) => { // Promise 构造函数内是同步 console.log(1) resolve() console.log(2) }) promise.then(() => { // promise.then 中的函数是异步 console.log(3) }) console.log(4)
// 1 2 4 3
<a name="yM0XM"></a>
###
<a name="6RX3N"></a>
## 宏任务(MacroTask)、微任务(MicroTask)
> 先微任务 后宏任务
<a name="E3IIS"></a>
### MacroTask(宏任务)
- `script` 全部代码、面试题中常见 console.log
- `setTimeout`
- `setInterval`
- `setImmediate`(浏览器暂时不支持,只有IE10支持,具体可见`[MDN](https://developer.mozilla.org/zh-CN/docs/Web/API/Window/setImmediate)`)
- `I/O`、`UI Rendering`。
<a name="zrE9Q"></a>
### MicroTask(微任务)
- `Process.nextTick(Node独有) `
- `Promise.then catch finally` (注意不是 Promise)
- `Object.observe(废弃)`
- `MutationObserver`
<a name="Diyjn"></a>
# 二、浏览器环境下的 Event Loop
<a name="4XFcC"></a>
## 1.
<a name="Fas5h"></a>
# 三、Node 环境下的 Event Loop
`Node`中的`Event Loop`是基于`libuv`实现的,而`libuv`是 `Node` 的新跨平台抽象层,libuv使用异步,事件驱动的编程方式,核心是提供`i/o`的事件循环和异步回调。libuv的`API`包含有时间,非阻塞的网络,异步文件操作,子进程等等。`Event Loop`就是在`libuv`中实现的。
┌───────────────────────────┐ ┌─>│ timers │ │ └─────────────┬─────────────┘ │ ┌─────────────┴─────────────┐ │ │ pending callbacks │ │ └─────────────┬─────────────┘ │ ┌─────────────┴─────────────┐ │ │ idle, prepare │ │ └─────────────┬─────────────┘ ┌───────────────┐ │ ┌─────────────┴─────────────┐ │ incoming: │ │ │ poll │<─────┤ connections, │ │ └─────────────┬─────────────┘ │ data, etc. │ │ ┌─────────────┴─────────────┐ └───────────────┘ │ │ check │ │ └─────────────┬─────────────┘ │ ┌─────────────┴─────────────┐ └──┤ close callbacks │ └───────────────────────────┘ ```
从上图中,大致看出node中的事件循环的顺序:
外部输入数据—>轮询阶段(poll)—>检查阶段(check)—>关闭事件回调阶段(close callback)—>定时器检测阶段(timer)—>I/O事件回调阶段(pending callbacks)—>闲置阶段(idle, prepare)—>轮询阶段(按照该顺序反复运行)…
- timers 阶段:这个阶段执行timer(setTimeout、setInterval)的回调
- pending callbacks 阶段:处理一些上一轮循环中的少数未执行的 I/O 回调
- idle, prepare 阶段:仅node内部使用
- poll 阶段:获取新的I/O事件, 适当的条件下node将阻塞在这里
- check 阶段:执行 setImmediate() 的回调
- close callbacks 阶段:执行 socket 的 close 事件回调