>[success]這里只介紹瀏覽器中的事件循環,node 中的事件循環放在 node.js 專題部分
[TOC]
## 靈魂三問
JavaScript 為什么是單線程的?JavaScript 為什么需要異步?JavaScript 單線程又是如何實現異步的?
<span style="font-size: 20px;">1.JavaScript 為什么是單線程的?</span>
現在有 2 個線程 process1 process2,假設 JavaScript 是多線程的,所以他們可以對同一個 dom 同時進行操作。process1 刪除了該 dom,而 process2 編輯了該 dom,同時下達 2 個矛盾的命令,瀏覽器究竟該如何執行呢?這樣想,JavaScript 為什么被設計成單線程應該就容易理解了吧。
<span style="font-size: 20px;">2.JavaScript 為什么需要異步?</span>
如果 JavaScript 中不存在異步,只能自上而下執行,如果上一行解析時間很長,那么下面的代碼就會被阻塞。對于用戶而言,阻塞就意味著"卡死",這樣就導致了很差的用戶體驗,所以 JavaScript 中存在異步執行。
<span style="font-size: 20px;">3.JavaScript 單線程又是如何實現異步的呢?</span>
是通過**事件循環**來實現的
>為了利用多核 CPU 的計算能力,HTML5 提出 Web Worker 標準,允許 JavaScript 腳本創建多個線程,但是子線程完全受主線程控制,且不得操作 DOM。所以,這個新標準并沒有改變 JavaScript 單線程的本質。
為什么說 JavaScript 是“非阻塞”的語言?非阻塞即:“程序不會因為某個任務而停下來”
```js
console.log("程序時間:" + new Date().getTime())
setTimeout(function () {
console.log("暫停一秒:" + new Date().getTime())
}, 1000)
console.log('這是暫停一秒之后的時間:' + new Date().getTime())
```
簡單來,如果上圖的 setTimeout 換成 C++ 的 sleep(1000) 之類的,那么 C++ 是會確實地“睡眠”那么段時間的,而 JS 不會。如果我就是想實現這個功能呢?可以利用 Promise 實現:
```js
async function test () {
console.log('start')
await sleep(3000)
console.log('3s has passed')
}
function sleep (ms) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve()
}, ms)
})
}
```
## 任務隊列
當遇到一個異步事件后,JavaScript 引擎并不會一直等待異步事件返回結果,而是會將這個事件掛在與執行棧不同的隊列中,我們稱之為任務隊列。
這些任務又被細分為宏任務和微任務
- 宏任務(macrotask):script(全局任務),setTimeout ,setInterval ,setImmediate **(node.js 獨有)**,I/O(磁盤讀寫或網絡通信) ,UI rendering(UI交互事件)
- 微任務(microtask):process.nextTick **(node.js 獨有)**, Promise.then, Object.observer(已廢棄), MutationObserver
## 事件循環(event loop)
這里首先要明確一點:瀏覽器是一個進程,其有多個線程(具體見 Broswer 章節)
一般情況下, 瀏覽器有如下五個線程:
* GUI 渲染線程
* JavaScript 引擎線程
* 瀏覽器事件觸發線程
* 定時器觸發線程
* 異步 HTTP 請求線程
GUI 渲染線程和 JavaScript 引擎線程是互斥的,其他線程相互之間都是可以并行執行的。
瀏覽器中,JavaScript 引擎循環地從任務隊列中讀取任務并執行,這種運行機制就叫做事件循環。

更準確地說,事件循環是按下面幾個步驟執行的:
1. 執行同步代碼,這屬于宏任務(初始時的同步代碼就是 script 整體代碼)
2. 執行棧為空,查詢是否有微任務 (microtask) 需要執行
3. 執行所有微任務
4. 必要的話渲染 UI
5. 然后開始下一輪 Event loop,執行宏任務中的異步代碼
## 例題
```js
setTimeout(function () {
console.log(1)
}, 0)
Promise.resolve(function () {
console.log(2)
})
new Promise(function (resolve) {
console.log(3)
})
console.log(4)
// 上述代碼的輸出結果是什么???
```
正確答案是`------------------->` 3 4 1
解釋如下:
``` js
// 遇到 setTimeout 將 setTimeout 回調放入宏任務隊列中
setTimeout(function () {
console.log(1)
}, 0)
// 遇到了 promise,但是并沒有 then 方法回調
// 所以這句代碼會在執行過程中進入我們當前的執行上下文 緊接著就出棧了
Promise.resolve(function () {
console.log(2)
})
// 遇到了一個 new Promise,Promise 有一個原則就是在初始化 Promise 的時候Promise 內部的構造器函數會立即執行,
// 因此在這里會立即輸出一個 3,所以這個 3 是第一個輸出的
new Promise(function (resolve) {
console.log(3)
})
// 然后第二個輸出 4 當代碼執行完畢后回去微任務隊列查找有沒有任務,
// 發現微任務隊列是空的,那么就去宏仁務隊列中查找,發現有一個我們剛剛放進去的setTimeout 回調函數,
// 那么就取出這個任務進行執行,所以緊接著輸出1
console.log(4)
```
太簡單了?來看看這題:
```js
console.log('begin'); // 1.begin
setTimeout(() => {
console.log('setTimeout 1'); // 3.setTimeout 1
Promise.resolve() // Promise.resolve().then :直接把 then 回調放入微任務隊列
.then(() => {
console.log('promise 1'); // 5.promise 1
setTimeout(() => {
console.log('setTimeout2'); // 8. setTimeout2
});
})
.then(() => {
console.log('promise 2'); // 7. promise 2 注意7比8要快,then 方法返回一個新的 Promise 對象
});
new Promise(resolve => {
console.log('a'); // 4. a
resolve();
}).then(() => {
console.log('b'); // 6. b
});
}, 0);
console.log('end'); // 2.end
```
- 序言 & 更新日志
- H5
- Canvas
- 序言
- Part1-直線、矩形、多邊形
- Part2-曲線圖形
- Part3-線條操作
- Part4-文本操作
- Part5-圖像操作
- Part6-變形操作
- Part7-像素操作
- Part8-漸變與陰影
- Part9-路徑與狀態
- Part10-物理動畫
- Part11-邊界檢測
- Part12-碰撞檢測
- Part13-用戶交互
- Part14-高級動畫
- CSS
- SCSS
- codePen
- 速查表
- 面試題
- 《CSS Secrets》
- SVG
- 移動端適配
- 濾鏡(filter)的使用
- JS
- 基礎概念
- 作用域、作用域鏈、閉包
- this
- 原型與繼承
- 數組、字符串、Map、Set方法整理
- 垃圾回收機制
- DOM
- BOM
- 事件循環
- 嚴格模式
- 正則表達式
- ES6部分
- 設計模式
- AJAX
- 模塊化
- 讀冴羽博客筆記
- 第一部分總結-深入JS系列
- 第二部分總結-專題系列
- 第三部分總結-ES6系列
- 網絡請求中的數據類型
- 事件
- 表單
- 函數式編程
- Tips
- JS-Coding
- Framework
- Vue
- 書寫規范
- 基礎
- vue-router & vuex
- 深入淺出 Vue
- 響應式原理及其他
- new Vue 發生了什么
- 組件化
- 編譯流程
- Vue Router
- Vuex
- 前端路由的簡單實現
- React
- 基礎
- 書寫規范
- Redux & react-router
- immutable.js
- CSS 管理
- React 16新特性-Fiber 與 Hook
- 《深入淺出React和Redux》筆記
- 前半部分
- 后半部分
- react-transition-group
- Vue 與 React 的對比
- 工程化與架構
- Hybird
- React Native
- 新手上路
- 內置組件
- 常用插件
- 問題記錄
- Echarts
- 基礎
- Electron
- 序言
- 配置 Electron 開發環境 & 基礎概念
- React + TypeScript 仿 Antd
- TypeScript 基礎
- React + ts
- 樣式設計
- 組件測試
- 圖標解決方案
- Storybook 的使用
- Input 組件
- 在線 mock server
- 打包與發布
- Algorithm
- 排序算法及常見問題
- 劍指 offer
- 動態規劃
- DataStruct
- 概述
- 樹
- 鏈表
- Network
- Performance
- Webpack
- PWA
- Browser
- Safety
- 微信小程序
- mpvue 課程實戰記錄
- 服務器
- 操作系統基礎知識
- Linux
- Nginx
- redis
- node.js
- 基礎及原生模塊
- express框架
- node.js操作數據庫
- 《深入淺出 node.js》筆記
- 前半部分
- 后半部分
- 數據庫
- SQL
- 面試題收集
- 智力題
- 面試題精選1
- 面試題精選2
- 問答篇
- 2025面試題收集
- Other
- markdown 書寫
- Git
- LaTex 常用命令
- Bugs