亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專欄INFORMATION COLUMN

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

wuaiqiu / 3184人閱讀

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

前言

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

正文

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

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

先看一個(gè)典型的例子:

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

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

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

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

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

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

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代表相對(duì)多帶帶占據(jù)evenloop過程一次循環(huán)的任務(wù),而microtask有可能在一次循環(huán)中執(zhí)行多個(gè))

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

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

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

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

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

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

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

常見異步操作對(duì)應(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í)行順序

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




    
outer
inner

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

click

promise

mutate

click

promise

mutate

timeout

timeout

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

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

運(yùn)行console.log("click"),輸出(1)click;

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

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

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

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

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

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

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

結(jié)束。

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

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

先說最終結(jié)果:

click

click

promise

mutate

promise

timeout

timeout

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

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

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

這是很關(guān)鍵的一點(diǎn)!??!就是這一個(gè)區(qū)別,導(dǎo)致了整個(gè)執(zhí)行結(jié)果的差異。
因?yàn)榍懊娴睦拥膱?zhí)行順序是:

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

函數(shù)出棧

點(diǎn)擊時(shí)觸發(fā)inner的onclcik

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

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

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

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

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

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

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

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

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

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

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

結(jié)束

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

結(jié)論

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

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

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

當(dāng)前call stack為空

ps:瀏覽器差異

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

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

Tasks, microtasks, queues and schedules

HTML Living Standard

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

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

轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/109389.html

相關(guān)文章

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

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

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

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

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

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

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

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

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

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

    Achilles 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<