前言:本人表述能力不太好,若是有不清晰的地方請必定回覆,我會改。
javascript
先肯定幾個概念:html
1.單線程:做爲瀏覽器腳本語言,用於解釋用戶操做,爲了不復雜性,js設計之初就是單線程的,這個是毋庸置疑的,也不會改變。
2.[同步,異步,阻塞,非阻塞](https://www.zhihu.com/question/19732473):
同步和異步關注的是消息通訊機制,區別在因而否等待結果返回。
阻塞和非阻塞關注的是程序在等待調用結果(消息,返回值)時的狀態。
3.執行棧,任務隊列:當調用js代碼的時候,會生成一個執行上下文。當開始調用一系列的方法,這些方法就存儲在執行棧中,等待依次執行。執行棧是執行同步任務。任務隊列存放執行當前上下文的異步任務
複製代碼
單線程意味着事件是一個一個執行的,前一個執行的時候,後一個須要等待着。但前一個如果網絡請求,可能長時間沒有回覆,就容易致使CPU浪費。執行完同步以後再執行異步,解決了一部分等待的問題。同步的任務在執行棧上執行,遇到異步就放進任務隊列裏,執行棧中的執行完後執行異步隊列。 所謂循環,就是上述事情在重複執行。它常常按照相似以下的方式來被實現:java
while (queue.waitForMessage()) {
queue.processNextMessage();
}
複製代碼
事件循環是一種運行機制,js引擎須要執行的時候,將須要執行的消息放進執行棧中,開闢執行上下文。事件循環就是一直重複循環檢查執行棧中是否有待執行的消息,當消息完整的執行完後,檢查是否有未執行的消息,有就繼續執行,沒有保持監聽。web
如下這種簡單代碼怎麼執行呢?chrome
function test () {
console.log(1);
console.log(2);
}
複製代碼
這個很好理解,代碼一行一行執行,入棧,執行完了以後出棧。console.log是當即執行的,因此在執行console.log後當即出棧了。最後只剩下test{}執行後進入下一次循環。
那若是有函數調用呢?執行棧如何處理?api
function foo(b) {
var a = 10;
return a + b + 11;
}
function bar(x) {
var y = 3;
return foo(x * y);
}
console.log(bar(7)); // 返回 42
複製代碼
函數調用的時候,執行棧中增長了被調用的函數的上下文,執行完被調用的函數後再繼續執行當前函數。 項目中有不少異步代碼。好比定時器,這些代碼會安排在任務隊列裏,等待當前主函數執行後依次執行。promise
console.log('這是開始');
setTimeout(function cb() {
console.log('這是來自第一個回調的消息');
});
console.log('這是一條消息');
setTimeout(function cb1() {
console.log('這是來自第二個回調的消息');
});
console.log('這是結束');
複製代碼
同步代碼順序執行,異步代碼另起任務隊列,只有在執行棧爲空的狀況下,任務隊列纔會開始工做。因此代碼中執行到setTimeout時,往任務隊列中添加事件,但並無執行。而當console.log('這是結束');執行出結果後執行任務隊列裏的任務。隊列是先進先出,因此首先執行cb,最後執行cb1;
ECMAScript 2015 引入了 Promises(也在 ES6 / ES2015 中引入) ,使用了做業隊列(Job Queue)概念,這是一種儘快執行異步函數的方法。異步隊列分爲兩種,消息隊列(也有叫宏任務)和做業隊列(也有叫微任務)。執行順序爲: 當前執行上下文 -> 微任務 -> 宏任務
根據事件循環理論分析代碼瀏覽器
var t = new Promise((resolve, reject) => {
console.log('宏事件');
resolve()
}).then(() => {
console.log('微事件');
})
setTimeout(() => {
console.log(t.then(opt => {
console.log('內層微事件')
}));
console.log('內層宏事件')
})
console.log('外層事件');
複製代碼
promise的方法當即執行,而其回調則是在微任務隊列中。setTimeout在宏任務隊列中。主任務執行結束後,執行微任務,再執行宏任務。
事件循環是js代碼執行順序的解釋,可是這裏面沒有說到何時會對用戶界面產生影響。 我在W3C的文章上看到的是這樣的:bash
-運行JS代碼
-運行微任務隊列
-執行佈局和IO工做
-運行宏任務隊列。
複製代碼
有結論,很美好,驗證下。網絡
<!DOCTYPE html>
<html lang="en">
<body>
<div class="outer" style="width:200px;height:200px;background-color: #ccc"></div>
</body>
<script> var outer = document.querySelector('.outer'); function onClick() { alert('start') outer.innerHTML = 'main func'; alert('main func do') setTimeout(function () { alert('setTimeout start') outer.innerHTML = 'setTimeout'; alert('setTimeout end') }, 10); Promise.resolve().then(function () { alert('Promise start') outer.innerHTML = 'Promise'; alert('Promise end') }); } outer.addEventListener('click', onClick); </script>
</html>
複製代碼
咱們在各種任務裏增長了DOM渲染,在DOM渲染的先後增長了彈窗(debug模式下不同的機制,不討論),在Chrome(其餘瀏覽器不同)上執行,發現Promisehtml end
打印出來後,inner
裏纔有了Promise
字樣,任務結束後,字樣改爲了setTimeout
。上面的結論在chrome是ok的。
參考文章: