一、基本概念
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 事件回调
