原文地址前端
https://github.com/ChenMingK/WebKnowledges-Notesnode
JavaScript 爲何是單線程的?JavaScript 爲何須要異步?JavaScript 單線程又是如何實現異步的?git
1.JavaScript 爲何是單線程的?github
如今有 2 個線程 process1 process2,假設 JavaScript 是多線程的,因此他們能夠對同一個 dom 同時進行操做。process1 刪除了該 dom,而 process2 編輯了該 dom,同時下達 2 個矛盾的命令,瀏覽器究竟該如何執行呢?這樣想,JavaScript 爲何被設計成單線程應該就容易理解了吧。promise
2.JavaScript 爲何須要異步?瀏覽器
若是 JavaScript 中不存在異步,只能自上而下執行,若是上一行解析時間很長,那麼下面的代碼就會被阻塞。對於用戶而言,阻塞就意味着"卡死",這樣就致使了不好的用戶體驗,因此 JavaScript 中存在異步執行。網絡
3.JavaScript 單線程又是如何實現異步的呢?多線程
是經過事件循環來實現的dom
爲了利用多核 CPU 的計算能力,HTML5 提出 Web Worker 標準,容許 JavaScript 腳本建立多個線程,可是子線程徹底受主線程控制,且不得操做 DOM。因此,這個新標準並無改變 JavaScript 單線程的本質。異步
爲何說 JavaScript 是「非阻塞」的語言?非阻塞即:「程序不會由於某個任務而停下來」
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 實現:
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 引擎並不會一直等待異步事件返回結果,而是會將這個事件掛在與執行棧不一樣的隊列中,咱們稱之爲任務隊列。
這些任務又被細分爲宏任務和微任務
這裏首先要明確一點:瀏覽器是一個進程,其有多個線程(具體見 Broswer 章節)
通常狀況下, 瀏覽器有以下五個線程:
GUI 渲染線程和 JavaScript 引擎線程是互斥的,其餘線程相互之間都是能夠並行執行的。
瀏覽器中,JavaScript 引擎循環地從任務隊列中讀取任務並執行,這種運行機制就叫作事件循環。
更準確地說,事件循環是按下面幾個步驟執行的:
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
解釋以下:
// 遇到 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)
太簡單了?來看看這題:
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