宏任務和微任務,同步異步,promis,await執行順序


本文作為EVENLOOP事件循環的延伸:

執行順序:

                         ------------循環----------

                         |                                              |

=====微任務==》{宏任務==》微任務==》瀏覽器渲染}=====>>>>

                                            ^

                                            ||

======監控線程===等待=====有返回進行返回=====>>>>>

宏任務和微任務都是任務隊列,只是用來存放東西的隊列。

主線程可以執行代碼,渲染UI之類的。

主線程空閑的時候就會去輪詢看看宏任務隊列有沒有要執行的任務,沒有就過,有就取出來執行。

宏任務執行完會去看微任務隊列。沒有就過,有就取出來執行。

微任務都是在宏任務執行完,空閑的時候執行。

也可以用下面的圖表示

 

 更細化的順序是: 同步--->微任務 ----> 異步

1. 什么是同步,什么是異步;

同步任務: 在主線程上排隊執行的任務,只有前一個任務執行完畢,才能執行下一個任務;

異步任務:不進入主線程,認識進入“任務隊列” 的任務,只有“任務隊列”通知主線程,某個異步任務可以執行了,該任務才會進入主線程執行

2. 什么是宏任務,什么微任務;

 查了一下,貌似除了微任務,別的都是宏任務! 

宏任務: 整體的 script  和  setTimeout  等!

而微任務貌似只有 : promise.then() (前端而言) node 好像還有一個---process.nextTick() 

先來看一段代碼,很多面試題有這個:

console.log('script start');

setTimeout(function() {
  console.log('setTimeout');
}, 0);

Promise.resolve().then(function() {
  console.log('promise1');
}).then(function() {
  console.log('promise2');
});

console.log('script end');

crome結果是:script start, script end, promise1, promise2, setTimeout,但是各瀏覽器不一致。

Microsoft Edge, Firefox 40, iOS Safari 及桌面 Safari 8.0.8 在 promise1 和 promise2 之前打印 setTimeout ——盡管這似乎是競爭條件導致的。奇怪的是,Firefox 39 和 Safari 8.0.7 是對的。

要知道為啥是這個順序,需要了解:事件循環的,tasks宏任務 和 microtasks微任務

宏任務

(macro)task,可以理解是每次執行棧執行的代碼就是一個宏任務(包括每次從事件隊列中獲取一個事件回調並放到執行棧中執行)。

瀏覽器為了能夠使得JS內部(macro)task與DOM任務能夠有序的執行,會在一個(macro)task執行結束后,在下一個(macro)task 執行開始前,對頁面進行重新渲染,流程如下:

(macro)task->渲染->(macro)task->...

宏任務包含:

script(整體代碼) setTimeout
setInterval
I/O UI交互事件
postMessage
MessageChannel
setImmediate(Node.js 環境)

微任務

microtask,可以理解是在當前 task 執行結束后立即執行的任務。也就是說,在當前task任務后,下一個task之前,在渲染之前。

所以它的響應速度相比setTimeout(setTimeout是task)會更快,因為無需等渲染。也就是說,在某一個macrotask執行完后,就會將在它執行期間產生的所有microtask都執行完畢(在渲染前)。

微任務包含:

Promise.then
Object.observe
MutaionObserver
process.nextTick(Node.js 環境)

運行機制

在事件循環中,每進行一次循環操作稱為 tick,每一次 tick 的任務處理模型是比較復雜的,但關鍵步驟如下:

  • 執行一個宏任務(棧中沒有就從事件隊列中獲取)
  • 執行過程中如果遇到微任務,就將它添加到微任務的任務隊列中
  • 宏任務執行完畢后,立即執行當前微任務隊列中的所有微任務(依次執行)
  • 當前宏任務執行完畢,開始檢查渲染,然后GUI線程接管渲染
  • 渲染完畢后,JS線程繼續接管,開始下一個宏任務(從事件隊列中獲取)

如圖:

 

總結:

  • tasks 按序執行,瀏覽器會在 tasks 之間執行渲染。
  • microtasks 按序執行,在下面情況時執行:
    • 在每個回調之后,只要沒有其它代碼正在運行。
    • 在每個 task 的末尾

async/await

async/await本質上還是基於Promise的一些封裝,而Promise是屬於微任務的一種。所以在使用await關鍵字與Promise.then相同。
async函數在await之前的代碼都是同步執行的,可以理解為await之前的代碼屬於new Promise時傳入的代碼,await之后的所有代碼都是在Promise.then中的回調

注意await的區別:

 打印結果為

script start
async1 start
async2
promise1
script end
async1 end
promise2
setTimeout

await之后的代碼必須等await語句執行完成后(包括微任務完成),才能執行后面的,也就是說,只有運行完await語句,才把await語句后面的全部代碼加入到微任務行列,所以,在遇到await promise時,必須等await promise函數執行完畢才能對await語句后面的全部代碼加入到微任務中。

總的來說,執行順序:

1、根據上下文執行同步數據

2、看到await,await+await后面的句子,同等於.then的回調。等正常的js執行完了以后,再走微任務await后的

4、然后是微任務.then后面的內容

6、宏任務(setimeout)

延伸:https://www.cnblogs.com/jiangyuzhen/p/11064408.html 里面很多面試題


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM