JavaScript同步、異步及事件循環
阿新 • • 發佈:2019-01-10
reject .sh imm -m user obs java server lan
更多文章
同步、異步
JS是單線程的,每次只能做一件事情。像以下這種情況,代碼會按順序執行,這個就叫同步。
console.log(1); console.log(2); console.log(3);
以下代碼會輸出2、3、1,像這種不按順序執行的,或者說代碼執行中間有時間間隙的,叫異步。
setTimeout(() => { console.log(1); }, 0); console.log(2); console.log(3);
事件循環
一個瀏覽器通常有以下幾個常駐的線程:
- 渲染引擎線程:該線程負責頁面的渲染
- JS引擎線程:負責JS的解析和執行
- 定時觸發器線程:處理定時事件,比如setTimeout, setInterval
- 事件觸發線程:處理DOM事件
- 異步http請求線程:處理http請求
渲染線程和JS引擎線程是不能同時進行的。也就是說在執行代碼時,渲染會掛起;渲染DOM時,代碼也不會執行。 雖然JS是單線程,但是瀏覽器是多線程的,在遇到像setTimeout、DOM事件、ajax等這種任務時,會轉交給瀏覽器的其他工作線程(上面提到的幾個線程)執行,執行完之後將回調函數放入到任務隊列。
// eventLoop是一個用作隊列的數組 // (先進,先出) var eventLoop = [ ]; var event; // “永遠”執行 while (true) { // 一次tick if (eventLoop.length > 0) {// 拿到隊列中的下一個事件 event = eventLoop.shift(); // 現在,執行下一個事件 event(); } }
我們可以用上面的代碼來想像一下JS的執行情況。
JS主線程,就像是一個while循環,會一直執行下去。在這期間,每次都會查看任務隊列有沒有需要執行的任務(回調函數)。在執行完一個任務之後, 會繼續下一個循環,直到任務隊列所有任務都執行完為止。
microtask(微任務)、macrotask(宏任務)
任務隊列又分微任務隊列和宏任務隊列
微任務
- Promise
- MutationObserver(Mutation Observer API 用來監視 DOM 變動)
- Object.observe()(已廢棄)
宏任務
- setTimeout
- setInterval
- setImmediate
- I\O
- UI rendering(DOM event)
執行過程
- 在JS執行完同步任務之後,會開始執行微任務隊列
- 在將所有的微任務執行完之後,會開始執行宏任務隊列
- 在執行完一個宏任務之後,跳出來,重新開始下一個循環(從1開始執行)
也就是說執行微任務隊列 會將隊列中的所有微任務執行完 而執行宏任務隊列 每次只執行一個宏任務 然後重新開始下一個循環 我們可以看看以下代碼
setTimeout(() => { console.log(3) new Promise((resolve, reject) => { console.log(5) resolve() }).then(console.log(6)) }, 0) setTimeout(() => { console.log(4) }, 0) new Promise((resolve, reject) => { console.log(1) resolve() }).then(console.log(2))
輸出是1 2 3 5 6 4
我們來分析一下代碼的執行過程
- 前面的兩個setTimeout都是宏任務,所以現在宏任務隊列有2個任務
- Promise裏面的代碼是同步任務,所以現在會馬上執行 輸出1
- Promise的then是微任務,所以現在微任務隊列有1個任務
- 在執行完同步任務之後,開始執行微任務,也就是console.log(2), 輸出2
- 在執行完微任務之後,會執行宏任務,第一個宏任務也就是第一個setTimeout
- 第一個setTimeout會先輸出3,然後輸出5,因為這兩個都是同步任務,然後遇到then,加入微任務隊列,宏任務執行完重新開始下一個循環。
- 因為沒有同步代碼,所以接著執行微任務,此時微任務隊列有1個任務(第6步加入), 宏任務隊列還有1個任務(第6步執行完了第一個宏任務)
- 執行微任務,輸出6
- 再執行宏任務,輸出4
JavaScript同步、異步及事件循環