event loop js事件循環 microtask macrotask

轉: 原文 http://blog.csdn.net/sjn0503/article/details/76087631javascript

----------------------------------------------------html

放個面試題,拋個磚:前端

console.log('start') const interval = setInterval(() => { console.log('setInterval') }, 0) setTimeout(() => { console.log('setTimeout 1') Promise.resolve() .then(() => { console.log('promise 3') }) .then(() => { console.log('promise 4') }) .then(() => { setTimeout(() => { console.log('setTimeout 2') Promise.resolve() .then(() => { console.log('promise 5') }) .then(() => { console.log('promise 6') }) .then(() => { clearInterval(interval) }) }, 0) }) }, 0) Promise.resolve() .then(() => { console.log('promise 1') }) .then(() => { console.log('promise 2') }) 

不着急揭曉答案,先分析java

首先知曉:web

js是單線程語言面試

也就是說一次就只能作一件事情。api

多數的網站不須要大量計算,程序花費的時間主要集中在磁盤 I/O 和網絡 I/O 上面promise

雖然SSD讀取很快,但和CPU處理指令的速度比起來也不在一個數量級上,並且網絡上一個數據包來回的時間更慢(注意過遊戲的延遲嗎)網絡

so: 一些cpu直接執行的任務就成了優先執行主線任務,而後須要io返回數據的任務就成了等待被執行的任務app

因此纔會有同步任務(synchronous)和異步任務(asynchronous)之分

同步任務:

在主線程上排隊執行的任務,前一個任務執行完畢,才能執行後一個任務;

異步任務:

不進入主線程、而進入」任務隊列」(task queue)的任務,只有」任務隊列」通知主線程,某個異步任務能夠執行了,該任務纔會進入主線程執行。

總之:

只要主線程空了,就會去讀取」任務隊列」,這就是JavaScript的運行機制

Microtasks Macrotasks

任務隊列不止一個,還有 microtasks 和 macrotasks

microtasks:

  • process.nextTick
  • promise
  • Object.observe
  • MutationObserver

macrotasks:

  • setTimeout
  • setInterval
  • setImmediate
  • I/O
  • UI渲染

whatwg規範:https://html.spec.whatwg.org/multipage/webappapis.html#task-queue

  • 一個事件循環(event loop)會有一個或多個任務隊列(task queue)
  • task queue 就是 macrotask queue
  • 每個 event loop 都有一個 microtask queue
  • task queue == macrotask queue != microtask queue
  • 一個任務 task 能夠放入 macrotask queue 也能夠放入 microtask queue 中

理解了這些定義以後,再看執行原理:

事件循環的順序,決定了JavaScript代碼的執行順序。它從script(總體代碼)開始第一次循環。以後全局上下文進入函數調用棧。直到調用棧清空(只剩全局),而後執行全部的micro-task。當全部可執行的micro-task執行完畢以後。循環再次從macro-task開始,找到其中一個任務隊列執行完畢,而後再執行全部的micro-task,這樣一直循環下去。

還要注意一點:

 

包裹在一個 script 標籤中的js代碼也是一個 task 確切說是 macrotask。

 

因此文首面試題的答案爲:

start 
promise 1 
promise 2 
setInterval 
setTimeout 1 
promise 3 
promise 4 
setInterval 
setTimeout 2 
promise 5 
promise 6

簡單來說,總體的js代碼這個macrotask先執行,同步代碼執行完後有microtask執行microtask,沒有microtask執行下一個macrotask,如此往復循環至結束.

--------------------------------------------------------2017年11月14日21:35:17---------------------------------------------------------------

另外,這篇文章也不錯

 

這個知識點。。。

https://blog.keifergu.me/2017/03/23/difference-between-javascript-macrotask-and-microtask/?hmsr=toutiao.io&utm_medium=toutiao.io&utm_source=toutiao.io

前端基礎進階系列

這個系統也超屌!!

http://www.jianshu.com/p/cd3fee40ef59

=======================

Macrotask 和 microtask 都是屬於上述的異步任務中的一種,咱們先看一下他們分別是哪些 API :

  • macrotaskssetTimeoutsetIntervalsetImmediate, I/O, UI rendering
  • microtasksprocess.nextTickPromisesObject.observe(廢棄), MutationObserver

 

任務隊列分爲 macrotasks 和 microtasks,而Promise中的then方法的函數會被推入 microtasks 隊列,而setTimeout的任務會被推入 macrotasks 隊列。在每一次事件循環中,macrotask 只會提取一個執行,而 microtask 會一直提取,直到 microtasks 隊列清空

注:通常狀況下,macrotask queues 咱們會直接稱爲 task queues,只有 microtask queues 纔會特別指明。

那麼也就是說若是個人某個 microtask 任務又推入了一個任務進入 microtasks 隊列,那麼在主線程完成該任務以後,仍然會繼續運行 microtasks 任務直到任務隊列耗盡。

而事件循環每次只會入棧一個 macrotask ,主線程執行完該任務後又會先檢查 microtasks 隊列並完成裏面的全部任務後再執行 macrotask。

=======================

測試代碼:

複製代碼
<script>
    console.log('script start');
    setTimeout(function() {
        console.log('setTimeout');
    },0);
    
    Promise.resolve().then(function() {
        console.log('promise');
    }).then(function() {
    console.log('promise2');
});

console.log('script end');

</script>
複製代碼
相關文章
相關標籤/搜索