国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

js異步從入門到放棄(三)- 異步任務(wù)隊列(task queues)

wuaiqiu / 3103人閱讀

摘要:前言本文是對于異步系列第一篇里提到的模型中,所提到的任務(wù)隊列的展開分析正文說明以下代碼均使用瀏覽器運行關(guān)于瀏覽器表現(xiàn)的差異在最后做補充。的函數(shù)執(zhí)行過程,重復(fù)前面的步驟,因此輸出此時執(zhí)行棧和均為空,存儲著兩個的回調(diào)函數(shù)。

前言

本文是對于異步系列第一篇里提到的evenloop模型中,所提到的任務(wù)隊列(task queues)的展開分析

正文

說明:以下代碼均使用chrome瀏覽器運行 關(guān)于瀏覽器表現(xiàn)的差異在最后做補充。

引子-奇怪的執(zhí)行順序

先看一個典型的例子:

console.log("script start")
// 第一個異步任務(wù)
setTimeout(()=>{
    console.log("setTimeout")
},0)

// 第二個異步任務(wù)
Promise.resolve().then(()=>{
    console.log("promise1")
}).then(()=>{ 
  console.log("promise2");
})
console.log("script end")
// 實際輸出結(jié)果: 
// script start
// script end
// promise1
// promise2
// setTimeout

根據(jù)之前說過的evenloop模型,首先輸出script startscript end沒有什么問題;
但是接下來卻發(fā)現(xiàn):
先執(zhí)行了Promise指定的callback而不是setTimeoutcallback-- Why?

兩種任務(wù)隊列(microtask queue&macrotask queue)

在之前討論evenloop模型時,粗略提到了任務(wù)隊列有2種類型:microtask queuemacrotask queue,他們的區(qū)別在于:

macrotask的執(zhí)行:是在evenloop的每次循環(huán)過程,取出macrotask queue中可執(zhí)行的第一個(注意不一定是第一個,因為我們說過例如setTimeout可以指定任務(wù)被執(zhí)行的最少延遲時間,當(dāng)前macrotask queue的首位保存的任務(wù)可能還沒有到執(zhí)行時間,所以queue只是代表callback插入的順序,不代表執(zhí)行時也要按照這個順序)。

microtask的執(zhí)行:在evenloop的每次循環(huán)過程之后,如果當(dāng)前的執(zhí)行棧(call stack)為空,那么執(zhí)行microtask queue中所有可執(zhí)行的任務(wù)

(某些文獻(xiàn)內(nèi)容中 直接把macrotask稱為task,或者某些中文文章中把它們翻譯成"微任務(wù)"和"宏任務(wù)",含義都是相似的:macrotask或者task代表相對多帶帶占據(jù)evenloop過程一次循環(huán)的任務(wù),而microtask有可能在一次循環(huán)中執(zhí)行多個)

現(xiàn)在回頭來解析前面的例子:

第一次執(zhí)行主函數(shù),輸出script start

遇到setTimeout,將對應(yīng)的callback插入macrotask queue

遇到promise,將對應(yīng)的callback插入microtask queue

輸出script end,主函數(shù)運行結(jié)束,執(zhí)行棧清空,此時開始檢查microtask queue,發(fā)現(xiàn)里面有可運行的任務(wù),因此按順序輸出promise1promise2

microtask queue執(zhí)行完,開始新一輪循環(huán),從macrotask queue取出setTimeout任務(wù)并執(zhí)行,輸出setTimeout

結(jié)束,呈現(xiàn)上面的輸出結(jié)果。

常見異步操作對應(yīng)的回調(diào)函數(shù)任務(wù)類型如下:

macrotask: setTimeout, setInterval, setImmediate, requestAnimationFrame, I/O, UI rendering

microtask: process.nextTick, Promises, Object.observe, MutationObserver

大概可以這樣區(qū)分:和html交互密切相關(guān)的異步操作,一般是macrotasks;由emcascript的相關(guān)接口返回的異步操作,一般是microtasks

如何判斷執(zhí)行順序

接下來看一個更復(fù)雜的例子,幫助理解不同異步任務(wù)的執(zhí)行順序




    
outer
inner

點擊inner部分,打開chrome的調(diào)試器,可以看到console打出的結(jié)果是:

click

promise

mutate

click

promise

mutate

timeout

timeout

接下來分析運行過程 (建議打開chrome單步調(diào)試,進(jìn)行觀察分析):

點擊inner,觸發(fā)對應(yīng)的onClick事件,此時inner對應(yīng)的onClick函數(shù)進(jìn)入執(zhí)行棧;

運行console.log("click"),輸出(1)click

運行setTimeout,macrotask queue添加對應(yīng)的console函數(shù)

運行Promise,此時microtask queue添加對應(yīng)的console函數(shù)

運行outer.setAttribute,觸發(fā)MutationObserver,microtask queue添加對應(yīng)的console函數(shù)(前面注明了MutationObserver創(chuàng)建的回調(diào)任務(wù)類型是microtask)

當(dāng)前函數(shù)執(zhí)行完畢,由于執(zhí)行棧清空,此時開始調(diào)度microtask queue,因此依次輸出(2)promise(3)mutate,此時當(dāng)前執(zhí)行棧call stackmicrotask queue均為空,但是macrotask queue里依然存儲著兩個東西--inner的Click觸發(fā)的任務(wù),以及先前setTimeout的回調(diào)函數(shù)。

inner的onclick函數(shù)雖然執(zhí)行完畢,但是由于事件冒泡,緊接著要觸發(fā)outeronClick的執(zhí)行函數(shù),因此setTimeout的回調(diào)暫時還無法執(zhí)行。

outeronClick函數(shù)執(zhí)行過程,重復(fù)前面的2-5步驟,因此輸出(4)click (5)promise (6)mutate

此時執(zhí)行棧call stackmicrotask queue均為空,macrotask queue存儲著兩個setTimeout的回調(diào)函數(shù)。,根據(jù)evenloop模型,開始分別執(zhí)行這兩個task,于是輸出了兩個(7)和(8)timeout

結(jié)束。

再次建議在調(diào)試器查看上面的步驟,尤其要注意觀察call stackmicrotask queue macrotask queue的變化,會更加直觀

在充分理解上面例子的基礎(chǔ)上,我們把點擊inner部分的這個操作,改成直接在js代碼的末尾加上innner.click(),請問結(jié)果是否一致呢?

先說最終結(jié)果:

click

click

promise

mutate

promise

timeout

timeout

與前一次的結(jié)果完全不同!
接下來再次進(jìn)入調(diào)試分析:

由于是直接執(zhí)行inner.click(),這次進(jìn)入inner綁定的onclick函數(shù)時,與前面是有所不同的:

通過chrome調(diào)試器可以看到,此時的call stack有兩層--除了onClick函數(shù)之外,還有一層匿名函數(shù),這層函數(shù)其實就是最外層的script,相當(dāng)于window.onload綁定的處理函數(shù)。

這是很關(guān)鍵的一點!!!就是這一個區(qū)別,導(dǎo)致了整個執(zhí)行結(jié)果的差異。
因為前面的例子的執(zhí)行順序是:

頁面加載后先運行了整個匿名函數(shù)

函數(shù)出棧

點擊時觸發(fā)inner的onclcik

此時onClick對應(yīng)的函數(shù)進(jìn)棧。

兩次執(zhí)行到onclick時的callstck區(qū)別如圖:

第一次,通過點擊inner觸發(fā)click

第二次,通過代碼直接觸發(fā)click

接下來分析本次的輸出順序:

重復(fù)前面例子中,步驟2-5,輸出一個(1)click

inneronClick函數(shù)執(zhí)行完畢,但是這次執(zhí)行棧并未清空,因為當(dāng)前匿名函數(shù)還在執(zhí)行棧里,因此無法開始調(diào)度microtask queue!!!(前面說過,microtask queue的調(diào)度必須在當(dāng)前執(zhí)行棧為空的情況下),因此,這時候會先進(jìn)入冒泡事件觸發(fā)的onClick

類似的,輸出(2)clcik之后,promise的回調(diào)函數(shù)進(jìn)入microtask queue

運行outer.setAttribute,觸發(fā)MutationObserver,但是此時microtask queue無法再次添加對應(yīng)的回調(diào)函數(shù)了,因為已經(jīng)有一個存在的監(jiān)聽函數(shù)在pengding

兩個onclick執(zhí)行完畢,執(zhí)行棧清空,接下來開始調(diào)度microtask queue,輸出(3)promise (4)mutate (5)promise

此時當(dāng)前執(zhí)行棧call stackmicrotask queue均為空,macrotask queue存儲著兩個setTimeout的回調(diào)函數(shù)。根據(jù)evenloop模型,開始分別執(zhí)行這兩個task,于是輸出了兩個(6)和(7)timeout

結(jié)束

這兩個例子的對比,著重說明了一點:
--microtask queue存儲的任務(wù),必須要在當(dāng)前函數(shù)執(zhí)行棧為空時才會開始調(diào)度。
完整內(nèi)容可參見html標(biāo)準(zhǔn)中的8.1.4部分

結(jié)論

macrotask會按順序執(zhí)行,并且有可能被中途插入瀏覽器render,例如上面的冒泡事件

microtask的執(zhí)行有兩個條件:

在每個macrotask結(jié)束之后

當(dāng)前call stack為空

ps:瀏覽器差異

上述代碼在chrome的瀏覽器下測試結(jié)果,可能和在某些版本的firefox和ie瀏覽器下不一致,在某些瀏覽器中可能會把promise的回調(diào)函數(shù)當(dāng)做mascrotask,但是:

普遍的共識把 Promise當(dāng)做是miscrotask,并且有比較充分的理由:如果把promose當(dāng)做是task(即mascrotask)將會導(dǎo)致一些性能問題--因為task的調(diào)度是可以被其他task相關(guān)的任務(wù)如Render打斷,還會因為與其他任務(wù)源的交互導(dǎo)致不確定性。
參考文獻(xiàn)

Tasks, microtasks, queues and schedules

HTML Living Standard

如果覺得寫得不好/有錯誤/表述不明確,都?xì)g迎指出
如果有幫助,歡迎點贊和收藏,轉(zhuǎn)載請征得同意后著明出處。如果有問題也歡迎私信交流,主頁有郵箱地址
如果覺得作者很辛苦,也歡迎打賞~

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/109389.html

相關(guān)文章

  • js異步入門放棄(實踐篇) — 常見寫法&面試題解析

    摘要:前文該系列下的前幾篇文章分別對不同的幾種異步方案原理進(jìn)行解析,本文將介紹一些實際場景和一些常見的面試題。流程調(diào)度里比較常見的一種錯誤是看似串行的寫法,可以感受一下這個例子判斷以下幾種寫法的輸出結(jié)果辨別輸出順序這類題目一般出現(xiàn)在面試題里。 前文 該系列下的前幾篇文章分別對不同的幾種異步方案原理進(jìn)行解析,本文將介紹一些實際場景和一些常見的面試題。(積累不太夠,后面想到再補) 正文 流程調(diào)度...

    Awbeci 評論0 收藏0
  • 大話javascript 4期:事件循環(huán)(2)

    摘要:只要指定過回調(diào)函數(shù),這些事件發(fā)生時就會進(jìn)入任務(wù)隊列,等待主線程讀取。三主線程從任務(wù)隊列中讀取事件,這個過程是循環(huán)不斷的,所以整個的這種運行機(jī)制又稱為事件循環(huán)。 一、任務(wù)隊列 同步任務(wù)與異步任務(wù)的由來 單線程就意味著,所有任務(wù)需要排隊,前一個任務(wù)結(jié)束,才會執(zhí)行后一個任務(wù)。如果前一個任務(wù)耗時很長,后一個任務(wù)就不得不一直等著。 如果排隊是因為計算量大,CPU忙不過來,倒也算了,但是很多時候C...

    李昌杰 評論0 收藏0
  • Event Loop - JS執(zhí)行機(jī)制

    摘要:心塞塞根據(jù)規(guī)范,事件循環(huán)是通過任務(wù)隊列的機(jī)制來進(jìn)行協(xié)調(diào)的。等便是任務(wù)源,而進(jìn)入任務(wù)隊列的是他們指定的具體執(zhí)行任務(wù)回調(diào)函數(shù)。然后當(dāng)前本輪的結(jié)束,主線程可以繼續(xù)取下一個執(zhí)行。 依然是:經(jīng)濟(jì)基礎(chǔ)決定上層建筑。 說明 首先,旨在搞清常用的同步異步執(zhí)行機(jī)制 其次,暫時不討論node.js的Event Loop執(zhí)行機(jī)制,以下關(guān)于瀏覽器的Event Loop執(zhí)行機(jī)制 最后,借鑒了很多前輩的研究文...

    muddyway 評論0 收藏0
  • javascript引擎執(zhí)行的過程的理解--執(zhí)行階段

    摘要:如果對語法分析和預(yù)編譯,還有疑問引擎執(zhí)行的過程的理解語法分析和預(yù)編譯階段。參與執(zhí)行過程的線程分別是引擎線程也稱為內(nèi)核,負(fù)責(zé)解析執(zhí)行腳本程序的主線程例如引擎。以上便是引擎執(zhí)行宏任務(wù)的整個過程。 一、概述 js引擎執(zhí)行過程主要分為三個階段,分別是語法分析,預(yù)編譯和執(zhí)行階段,上篇文章我們介紹了語法分析和預(yù)編譯階段,那么我們先做個簡單概括,如下: 1、語法分析: 分別對加載完成的代碼塊進(jìn)行語法...

    SnaiLiu 評論0 收藏0
  • javascript引擎執(zhí)行的過程的理解--執(zhí)行階段

    摘要:如果對語法分析和預(yù)編譯,還有疑問引擎執(zhí)行的過程的理解語法分析和預(yù)編譯階段。參與執(zhí)行過程的線程分別是引擎線程也稱為內(nèi)核,負(fù)責(zé)解析執(zhí)行腳本程序的主線程例如引擎。以上便是引擎執(zhí)行宏任務(wù)的整個過程。一、概述 js引擎執(zhí)行過程主要分為三個階段,分別是語法分析,預(yù)編譯和執(zhí)行階段,上篇文章我們介紹了語法分析和預(yù)編譯階段,那么我們先做個簡單概括,如下: 1、語法分析: 分別對加載完成的代碼塊進(jìn)行語法檢驗,語...

    Achilles 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<