寫在前面:壓力只是暫時的,都會過去,這是我一周以為聽到的最頓悟的一句話了吧~
1.引言
js作為單線程的運行機制,必定有自己的運行順序,在聽了一次分享后,也好奇這種運行的機制到底是什么?
js可分為同步任務和異步任務,對於同步的任務,我們當然知道按照順序進行執行,但是對於異步的操作,會有一個優先級的執行順序,分別為宏任務和微任務
宏任務(macrotasks)和微任務(microtasks)??包含什么?
macrotasks: setTimeout, setInterval, setImmediate, I/O, UI rendering
microtasks: process.nextTick, Promises, Object.observe(廢棄), MutationObserver
在js執行時候,一個主線程里面都會有一個事件循環(消息循環|運行循環)和事件隊列,存放各種要處理的事件信息,通過這個循環不斷處理這些事件信息或消息。
談到這里,很明顯知道,其實出現宏任務和微任務和瀏覽器以及js的執行機制有很大的關系。了解js的執行機制
2.javascript的執行runtime

這是一張javascript的執行機制圖
(1) javaScript Engine,Chrome 的引擎就是 V8
(2) Web APIs,DOM 的操作,AJAX,Timeout 等實際上調用的都是這里提供的
(3)Callback Queue,回調的隊列,也就是剛剛所有的 Web APIs 里面的回調函數,實際上都是放在這里排隊的
(4) EventLoop,事件循環,也就是剛所說的宏任務和微任務的容器
call Stack
本身就是一個調用棧(就像瀏覽器中的JavaScript解釋器),追蹤函數執行流的一種機制,當執行環境調用了多個函數時,通過調用棧,我們可以追蹤到哪一個函數在執行,執行的函數體中又調用了哪些函數。
每調用一個函數,解釋器就會把該函數添加進調用棧並開始執行。
正在調用棧中執行的函數還調用了其它函數,那么新函數也將會被添加進調用棧,一旦這個函數被調用,便會立即執行。
當前函數執行完畢后,解釋器將其清出調用棧,繼續執行當前執行環境下的剩余的代碼。
當分配的調用棧空間被占滿時,會引發“堆棧溢出”。
是存放執行的重要條件,也是因為只有一個調用棧,所以被稱為單線程
callback quene
在js的編譯階段,將一些時間防止在執行隊列中
EventLoop 事件循環
一個作用就是將callback quene隊列里的執行事件放在在call stack中,執行
3.事件循環
js是單線程的,執行較長的js時候,頁面會卡死,無法響應,但是所有的操作都會被記住到另外的隊列。比如:點擊了一個元素,不會立刻的執行,但是等到js加載完畢后就會執行剛才點擊的操作,能夠知道有一個隊列記錄了所有有待執行的操作,這個隊列分為微觀和宏觀。微觀會比宏觀執行得更快。
稱為事件循環的原因大多來源於源碼
while (queue.waitForMessage()) {
queue.processNextMessage();
}
可以看到入伏哦有消息就會執行,沒消息就會繼續等待

4.宏任務和微任務
是一種人們定義的執行名字,

如何區分宏任務和微任務呢?划分的標准是什么
- Chromium 自定義消息
- Socket 或者文件等 IO 消息
- UI 相關的消息
- 是個內存回收的清理任務,使用過 Java 的童鞋應該都很熟悉,只是在 JavaScript 這是V8內部調用的
- 就是普通的回調,MutationObserver 也是這一類
- Callable
- 包括 Fullfiled 和 Rejected 也就是 Promise 的完成和失敗
- Thenable 對象的處理任務
- 宏任務 Macrotasks 就是參與了事件循環的異步任務。
- 微任務 Microtasks 就是沒有參與事件循環的“異步”任務。
-
console.log("開始執行1") console.log(Object.keys({a: 1})); setTimeout(() => { console.log(Object.keys({b: 2})); var promise = new Promise((resolve, reject) => { resolve(1); }); promise.then(res => { //后續增加測試 setTimeout(() => { console.log(Object.keys({e:1})) }, 0); console.log(Object.keys({c: 1})); }); }, 2000); console.log("結束執行2")
微觀任務是在當前JS調用執行完了之后立刻執行的,是同步的,在同一個調用棧里,沒有多線程異步,如這里包括promise.then在內的setTimeout回調里的代碼都是在DOMTimer.Fired執行的,只是說then被放到了當前要執行的整一個異步回調函數的最后面執行。所以setTimeout 0是給主線程的消息循環任務隊列添加了一個新的task(回調),而promise.then是在當前task的V8里的microtask插入了一個任務。那么肯定是當前正在執行的task執行完了才執行下一個task.vue的nextTick 也是一個微觀任務
let img = new Image();
img.src = 'image01.png?_=' + Date.now();
img.onload = function () { console.log('img ready'); } console.log(Object.keys({e: 1}));
