世界上最高效的學習方法,是把你學到的知識,經過本身理解的方式,教會另外一我的。共勉前端
不少人都據說過 JS 是單線程的,可是線程是什麼?進程又是什麼?它們之間有什麼關係? promise
從本質上說,這兩個名詞都是關於 CPU 時間片的一個描述。進程描述了 CPU 在運行指令、加載及保存上下文所需時間。線程則是進程中更小的一個單位,描述執行一段指令所需時間。 這個概念放到瀏覽器上說,打開一個 Tab 頁是一個進程,其中的 UI 渲染、JS 代碼執行、DOM 加載、Http 請求等都是一個個線程;放到 JS 代碼執行上來講,一個函數是一個進程,而函數內變量的定義、其餘函數的調用等任務都是一個個線程。瀏覽器
JS 執行是單線程的,意味着 JS 在執行代碼的時候一次只能處理一個任務,必須按隊列順序逐個執行。JS 的主要功效是處理前端交互,其中就包括操做 DOM 節點。試想若 JS 是多線程,在處理網頁交互時,一個線程須要刪除 DOM 節點,另外一個線程倒是要操做同一個 DOM 節點,這樣該如何判斷先執行哪一個線程?但若隊列中存在多個任務,上一個任務的執行會阻塞下一個任務,致使代碼執行效率低下。就像 AJAX 請求線程,發出請求後須要等待響應結果,期間 CPU 倒是空閒的。對此,JS的事件循環機制(Event Loop)很好地解決了問題。bash
JavaScript 將任務分爲兩種:同步任務和異步任務。多線程
同步任務:執行完後能當即得出結果的任務。同步任務在主線程中執行,在執行過程當中產生堆棧,堆中存儲複雜數據類型(Object),棧中存儲基本數據類型(String、Number、Boolean、Null、Undefined、Symbol)。異步
異步任務:執行後沒法當即得出結果,須要等待一段時間得到相應的任務。其中又分爲宏任務(Macrotask)
和微任務(Microtask)
。函數
宏任務:主程序Script
、setTimeout
、setInterval
、setImmediate
、I/O操做(mouse click、keypress、network event)
、UI渲染
、requestAnimationTrame
等。oop
微任務:promise
、MutationObserver
、process.nextTick()
、mutation
、Object.oberse
等。學習
實例:ui
const a = 1;
const arr = [1, 2, 3];
console.log('1');
console.log(a, arr);
setTimeout(function () {
console.log('2');
process.nextTick(function () {
console.log('3');
});
new Promise(function (resolve) {
console.log('4');
resolve();
}).then(function () {
console.log('5');
});
}, 100);
new Promise(function (resolve) {
console.log('6');
resolve();
}).then(function () {
console.log('7');
});
process.nextTick(function () {
console.log('8');
setImmediate(() => {
console.info('9');
});
new Promise(function (resolve) {
console.log('10');
resolve();
}).then(function () {
console.log('11');
});
setTimeout(function () {
console.log('12');
setImmediate(() => {
console.info('13');
});
process.nextTick(function () {
console.log('14');
});
new Promise(function (resolve) {
console.log('15');
resolve();
}).then(function () {
console.log('16');
});
}, 100);
process.nextTick(function () {
console.log('17');
});
});
執行結果:1 1,[1,2,3] 6 7 8 10 17 11 9 2 4 5 3 12 15 16 13 14
複製代碼
解析:
上述代碼就至關於一個進程,裏面的函數執行即爲一個個線程,一個個任務。