衆所周知,js 是一門單線程的非阻塞的腳本語言。javascript
單線程:只有一個調用棧,同一時刻只能幹一件事,代碼是一段一段執行的。java
調用棧:是一個數據結構,記錄咱們程序運行到哪個階段了,若是調用了函數就進棧,若是函數返回結果,就出棧(進棧出棧)。node
非阻塞:代碼須要進行一項異步任務的時候,主線程會掛起這個任務,而後在異步任務返回結果的時候,再根據一段的規則去執行相應的回調。web
爲何是單線程的?
這是由於 js 創立之初的目的就在於與瀏覽器交互,而瀏覽器要大量操做 dom,試想一下,若是同時對某個 dom 節點進行修改和刪除的操做,那會發生什麼呢?因此決定了 js 只能是單線程的。ajax
爲何非阻塞呢?
咱們在頁面中一般會發大量的請求,獲取後端的數據去渲染頁面。由於瀏覽器是單線程的,試想一下,當咱們發出異步請求的時候,阻塞了,後面的代碼都不執行了,那頁面可能出現長時間白屏,極度影響用戶體驗。後端
這裏,咱們只談論 Google 的 js 引擎---V8 引擎(nodeJS 也是 v8 引擎)。api
主要是由兩部分組成:promise
stack
裏執行callback queue
裏。stack
中的全部任務都執行完畢,主線程處於閒置狀態時,主線程會去查找callback queue
是否有任務。若是有,那麼主線程會從中取出回調(此處區分宏任務與微任務)放入stack
中,而後執行其中的同步代碼...,如此反覆。console.log(1); setTimeout(function a() { console.log(2); }, 0); new Promise(function (resolve, reject) { console.log(5); resolve(); }).then(function () { console.log(6); }); new Promise(function (resolve, reject) { resolve(); }).then(function () { console.log(7); }); console.log(3);
結果:1,5,3,6,2瀏覽器
全部
微任務隊列中的事件。因此,先打印 6,再打印 7,在打印 2.let promiseGlobal = new Promise(function (resolve) { console.log(1); resolve("2"); }); console.log(3); promiseGlobal.then(function (data) { console.log(data); let setTimeoutInner = setTimeout(function (_) { console.log(4); }, 1000); let promiseInner = new Promise(function (resolve) { console.log(5); resolve(6); }).then(function (data) { console.log(data); }); }); let setTimeoutGlobal = setTimeout(function (_) { console.log(7); let promiseInGlobalTimeout = new Promise(function (resolve) { console.log(8); resolve(9); }).then(function (data) { console.log(data); }); }, 1000);
執行順序是 1,3,2,5,6,間隔一秒,7,8,9,4網絡
解答以下:
⚠️易錯點: 之因此把這道題拿出來說,是由於這道題涉及到屢次事件循環,不少同窗容易搞混的點。
全部
微任務隊列中的事件,再去宏任務隊列中取出一個
事件以前
執行1.主進程
2.第三方插件進程
3.GPU 進程
4.渲染進程,就是咱們說的瀏覽器內核(最重要
)
1.JS 引擎線程
2.GUI 渲染線程
3.http 請求線程
4.事件處理線程(鼠標點擊、ajax 等)
5.定時器觸發線程
互斥
的?document.body.style = "background:black"; document.body.style = "background:red"; document.body.style = "background:blue"; document.body.style = "background:grey";
結果:背景直接變成灰色
分析:Call Stack 清空的時候,執行,執行到了 document.body.style = 'background:grey';這時,前面的代碼都被覆蓋了,此時 dom 渲染,背景色是灰色
document.body.style = "background:blue"; console.log(1); Promise.resolve().then(function () { console.log(2); document.body.style = "background:black"; }); console.log(3);
結果:背景直接變成黑色
分析:document.body.style = 'background:blue'是同步代碼,document.body.style = 'background:black'是微任務,此時微任務執行完,纔會進行 dom 渲染,因此背景色是黑色
document.body.style = "background:blue"; setTimeout(function () { document.body.style = "background:black"; }, 0);
結果:背景先一閃而過藍色,而後變成黑色
分析:document.body.style = 'background:blue';是同步代碼,document.body.style = 'background:black'是宏任務,因此 dom 在同步代碼執行完,宏任務執行以前會渲染一次。而後宏任務執行完又會渲染一次。2 次渲染,因此纔會呈現背景先一閃而過藍色,而後變成黑色,這種效果。
1.先把Call Stack清空 2.而後執行當前的微任務 3.接下來DOM渲染 微任務在dom渲染`以前`執行,宏任務在dom渲染`以後`執行。
⚠️ 注意:如下內容 node 的版本大於等於 11.0.0
解釋:
優先
於其餘 microtask 執行。setTimeout(funciton(){console.log(1)}); setImmediate(function(){console.log(2)}); process.nextTick(function(){console.log(3)}); Promise.resolve().then(function(){console.log(4)}); (function() {console.log(5)})();
打印結果:5,3,4,1,2
清空
了,該執行的回調函數都執行了,事件循環纔會進入下一個
階段。由於執行完2個定時器,回調都進入宏任務隊列了。而後開始事件循環,由於宏任務是一個個執行的,因此先把第一個定時器的回調放入調用棧中,執行完time1,把微任務放入微任務隊列中。
這是調用棧清空,又開始事件循環,這時候有微任務promise1,和第二個宏任務。由於微任務在宏任務以前執行,因此先執行promise1,
這是調用棧又清空,又開始事件循環。執行第二個宏任務,打印,time2,promise2
由於已經在timer階段了,因此。先執行完time階段,time1,time2,而後看到微任務,執行微任務。