您好,登錄后才能下訂單哦!
這篇文章主要介紹JavaScript中執(zhí)行上下文和執(zhí)行機(jī)制的示例分析,文中介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們一定要看完!
說(shuō)js
中的執(zhí)行上下文和js
執(zhí)行機(jī)制之前我們來(lái)說(shuō)說(shuō)線程和進(jìn)程
用官方的話術(shù)來(lái)說(shuō) 線程
是CPU
調(diào)度的最小單位。
用官方的話術(shù)來(lái)說(shuō) 進(jìn)程
是CPU
資源分配的最小單位。
線程
是建立在進(jìn)程
的基礎(chǔ)上的一次程序運(yùn)行單位,通俗點(diǎn)解釋線程
就是程序中的一個(gè)執(zhí)行流,一個(gè)進(jìn)程
可以有一個(gè)或多個(gè)線程
。
一個(gè)進(jìn)程
中只有一個(gè)執(zhí)行流稱作單線程
,即程序執(zhí)行時(shí),所走的程序路徑按照連續(xù)順序排下來(lái),前面的必須處理好,后面的才會(huì)執(zhí)行。
一個(gè)進(jìn)程
中有多個(gè)執(zhí)行流稱作多線程
,即在一個(gè)程序中可以同時(shí)運(yùn)行多個(gè)不同的線程
來(lái)執(zhí)行不同的任務(wù), 也就是說(shuō)允許單個(gè)程序創(chuàng)建多個(gè)并行執(zhí)行的線程
來(lái)完成各自的任務(wù)。
下面筆者舉一個(gè)簡(jiǎn)單的例子,比如我們打開(kāi)qq音樂(lè)
聽(tīng)歌,qq音樂(lè)
就可以理解為一個(gè)進(jìn)程,在qq音樂(lè)
中我們可以邊聽(tīng)歌邊下載這里就是多線程,聽(tīng)歌是一個(gè)線程,下載是一個(gè)線程。如果我們?cè)俅蜷_(kāi)vscode
來(lái)寫(xiě)代碼這就是另外一個(gè)進(jìn)程了。
進(jìn)程之間相互獨(dú)立,但同一進(jìn)程下的各個(gè)線程間有些資源是共享的。
線程的生命周期會(huì)經(jīng)歷五個(gè)階段。
新建狀態(tài): 使用 new
關(guān)鍵字和 Thread
類(lèi)或其子類(lèi)建立一個(gè)線程對(duì)象后,該線程對(duì)象就處于新建狀態(tài)。它保持這個(gè)狀態(tài)直到程序 start()
這個(gè)線程。
就緒狀態(tài): 當(dāng)線程對(duì)象調(diào)用了 start()
方法之后,該線程就進(jìn)入就緒狀態(tài)。就緒狀態(tài)的線程處于就緒隊(duì)列中,只要獲得 CPU
的使用權(quán)就可以立即運(yùn)行。
運(yùn)行狀態(tài): 如果就緒狀態(tài)的線程獲取 CPU
資源,就可以執(zhí)行 run()
,此時(shí)線程便處于運(yùn)行狀態(tài)。處于運(yùn)行狀態(tài)的線程最為復(fù)雜,它可以變?yōu)樽枞麪顟B(tài)、就緒狀態(tài)和死亡狀態(tài)。
阻塞狀態(tài): 如果一個(gè)線程執(zhí)行了 sleep(睡眠)
、suspend(掛起)
、wait(等待)
等方法,失去所占用資源之后,該線程就從運(yùn)行狀態(tài)進(jìn)入阻塞狀態(tài)。在睡眠時(shí)間已到或獲得設(shè)備資源后可以重新進(jìn)入就緒狀態(tài)??梢苑譃槿N:
等待阻塞:運(yùn)行狀態(tài)中的線程執(zhí)行 wait()
方法,使線程進(jìn)入到等待阻塞狀態(tài)。
同步阻塞:線程在獲取 synchronized
同步鎖失敗(因?yàn)橥芥i被其他線程占用)。
其他阻塞:通過(guò)調(diào)用線程的 sleep()
或 join()
發(fā)出了 I/O
請(qǐng)求時(shí),線程就會(huì)進(jìn)入到阻塞狀態(tài)。當(dāng) sleep()
狀態(tài)超時(shí),join()
等待線程終止或超時(shí),或者 I/O
處理完畢,線程重新轉(zhuǎn)入就緒狀態(tài)。
死亡狀態(tài): 一個(gè)運(yùn)行狀態(tài)的線程完成任務(wù)或者其他終止條件發(fā)生時(shí),該線程就切換到終止?fàn)顟B(tài)。
JS
是單線程。JS
作為瀏覽器腳本語(yǔ)言其主要用途是與用戶互動(dòng),以及操作DOM
。這決定了它只能是單線程,否則會(huì)帶來(lái)很復(fù)雜的同步問(wèn)題。比如,假定JavaScript
同時(shí)有兩個(gè)線程,一個(gè)線程在某個(gè)DOM
節(jié)點(diǎn)上添加內(nèi)容,另一個(gè)線程刪除了這個(gè)節(jié)點(diǎn),這時(shí)瀏覽器應(yīng)該以哪個(gè)線程為準(zhǔn)?
當(dāng) JS
引擎解析到可執(zhí)行代碼片段(通常是函數(shù)調(diào)用階段)的時(shí)候,就會(huì)先做一些執(zhí)行前的準(zhǔn)備工作,這個(gè) “準(zhǔn)備工作” ,就叫做 "執(zhí)行上下文(execution context 簡(jiǎn)稱 EC
)" 或者也可以叫做執(zhí)行環(huán)境。
javascript
中有三種執(zhí)行上下文類(lèi)型,分別是:
全局執(zhí)行上下文 這是默認(rèn)或者說(shuō)是最基礎(chǔ)的執(zhí)行上下文,一個(gè)程序中只會(huì)存在一個(gè)全局上下文,它在整個(gè) javascript
腳本的生命周期內(nèi)都會(huì)存在于執(zhí)行堆棧的最底部不會(huì)被棧彈出銷(xiāo)毀。全局上下文會(huì)生成一個(gè)全局對(duì)象(以瀏覽器環(huán)境為例,這個(gè)全局對(duì)象是 window
),并且將 this
值綁定到這個(gè)全局對(duì)象上。
函數(shù)執(zhí)行上下文 每當(dāng)一個(gè)函數(shù)被調(diào)用時(shí),都會(huì)創(chuàng)建一個(gè)新的函數(shù)執(zhí)行上下文(不管這個(gè)函數(shù)是不是被重復(fù)調(diào)用的)。
Eval 函數(shù)執(zhí)行上下文 執(zhí)行在 eval
函數(shù)內(nèi)部的代碼也會(huì)有它屬于自己的執(zhí)行上下文,但由于并不經(jīng)常使用 eval
,所以在這里不做分析。
前面我們說(shuō)到js
在運(yùn)行的時(shí)候會(huì)創(chuàng)建執(zhí)行上下文,但是執(zhí)行上下文是需要存儲(chǔ)的,那用什么來(lái)存儲(chǔ)呢?就需要用到棧數(shù)據(jù)結(jié)構(gòu)了。
棧是一種先進(jìn)后出的數(shù)據(jù)結(jié)構(gòu)。
所以總結(jié)來(lái)說(shuō)用來(lái)存儲(chǔ)代碼運(yùn)行時(shí)創(chuàng)建的執(zhí)行上下文就是執(zhí)行棧。
在執(zhí)行一段代碼時(shí),JS
引擎會(huì)首先創(chuàng)建一個(gè)執(zhí)行棧,用來(lái)存放執(zhí)行上下文。
然后 JS
引擎會(huì)創(chuàng)建一個(gè)全局執(zhí)行上下文,并 push
到執(zhí)行棧中, 這個(gè)過(guò)程 JS
引擎會(huì)為這段代碼中所有變量分配內(nèi)存并賦一個(gè)初始值(undefined),在創(chuàng)建完成后,JS
引擎會(huì)進(jìn)入執(zhí)行階段,這個(gè)過(guò)程 JS
引擎會(huì)逐行的執(zhí)行代碼,即為之前分配好內(nèi)存的變量逐個(gè)賦值(真實(shí)值)。
如果這段代碼中存在 function
的調(diào)用,那么 JS
引擎會(huì)創(chuàng)建一個(gè)函數(shù)執(zhí)行上下文,并 push
到執(zhí)行棧中,其創(chuàng)建和執(zhí)行過(guò)程跟全局執(zhí)行上下文一樣。
當(dāng)一個(gè)執(zhí)行棧執(zhí)行完畢后該執(zhí)行上下文就會(huì)從棧中彈出,接下來(lái)會(huì)進(jìn)入下一個(gè)執(zhí)行上下文。
下面筆者來(lái)舉個(gè)例子,假如在我們的程序中有如下代碼
console.log("Global Execution Context start"); function first() { console.log("first function"); second(); console.log("Again first function"); } function second() { console.log("second function"); } first(); console.log("Global Execution Context end");
上面的例子我們簡(jiǎn)單來(lái)分析下
首先會(huì)創(chuàng)建一個(gè)執(zhí)行棧
然后會(huì)創(chuàng)建一個(gè)全局上下文,并將該執(zhí)行上下文push
到執(zhí)行棧中
開(kāi)始執(zhí)行,輸出Global Execution Context start
遇到first
方法,執(zhí)行該方法,創(chuàng)建一個(gè)函數(shù)執(zhí)行上下文并push
到執(zhí)行棧
執(zhí)行first
執(zhí)行上下文,輸出first function
遇到second
方法,執(zhí)行該方法,創(chuàng)建一個(gè)函數(shù)執(zhí)行上下文并push
到執(zhí)行棧
執(zhí)行second
執(zhí)行上下文,輸出second function
second
執(zhí)行上下文執(zhí)行完畢,從棧中彈出,進(jìn)入到下一個(gè)執(zhí)行上下文first
執(zhí)行上下文
first
執(zhí)行上下文繼續(xù)執(zhí)行,輸出Again first function
first
執(zhí)行上下文執(zhí)行完畢,從棧中彈出,進(jìn)入到下一個(gè)執(zhí)行上下文全局執(zhí)行上下文
全局執(zhí)行上下文繼續(xù)執(zhí)行,輸出Global Execution Context end
我們用一張圖來(lái)總結(jié)
好了。說(shuō)完執(zhí)行上下文和執(zhí)行棧我們?cè)賮?lái)說(shuō)說(shuō)js
的執(zhí)行機(jī)制
說(shuō)到js
的執(zhí)行機(jī)制,我們就需要了解js
中同步任務(wù)和異步任務(wù)、宏任務(wù)和微任務(wù)了。
在js
中,任務(wù)分為同步任務(wù)和異步任務(wù),那什么是同步任務(wù)什么是異步任務(wù)呢?
同步任務(wù)指的是,在主線程上排隊(duì)執(zhí)行的任務(wù),只有前一個(gè)任務(wù)執(zhí)行完畢,才能執(zhí)行后一個(gè)任務(wù)。
異步任務(wù)指的是,不進(jìn)入主線程、而進(jìn)入"任務(wù)隊(duì)列"的任務(wù)(任務(wù)隊(duì)列中的任務(wù)與主線程并列執(zhí)行),只有當(dāng)主線程空閑了并且"任務(wù)隊(duì)列"通知主線程,某個(gè)異步任務(wù)可以執(zhí)行了,該任務(wù)才會(huì)進(jìn)入主線程執(zhí)行。由于是隊(duì)列存儲(chǔ)所以滿足先進(jìn)先出規(guī)則。常見(jiàn)的異步任務(wù)有我們的setInterval
、setTimeout
、promise.then
等。
前面介紹了同步任務(wù)和異步任務(wù),下面我們來(lái)說(shuō)說(shuō)事件循環(huán)。
同步和異步任務(wù)分別進(jìn)入不同的執(zhí)行"場(chǎng)所",同步的進(jìn)入主線程,只有前一個(gè)任務(wù)執(zhí)行完畢,才能執(zhí)行后一個(gè)任務(wù)。異步任務(wù)不進(jìn)入主線程而是進(jìn)入 Event Table
并注冊(cè)函數(shù)。
當(dāng)指定的事情完成時(shí),Event Table
會(huì)將這個(gè)函數(shù)移入 Event Queue
。Event Queue
是隊(duì)列數(shù)據(jù)結(jié)構(gòu),所以滿足先進(jìn)先出規(guī)則。
主線程內(nèi)的任務(wù)執(zhí)行完畢為空,會(huì)去 Event Queue
讀取對(duì)應(yīng)的函數(shù),進(jìn)入主線程執(zhí)行。
上述過(guò)程會(huì)不斷重復(fù),也就是常說(shuō)的 Event Loop(事件循環(huán))。
我們用一張圖來(lái)總結(jié)下
下面筆者簡(jiǎn)單來(lái)介紹個(gè)例子
function test1() { console.log("log1"); setTimeout(() => { console.log("setTimeout 1000"); }, 1000); setTimeout(() => { console.log("setTimeout 100"); }, 100); console.log("log2"); } test1(); // log1、log2、setTimeout 100、setTimeout 1000
我們知道在js中會(huì)優(yōu)先執(zhí)行同步任務(wù)再執(zhí)行異步任務(wù),所以上面的例子會(huì)先輸出log1、log2
同步任務(wù)執(zhí)行完后會(huì)執(zhí)行異步任務(wù),所以延遲100
毫秒的回調(diào)函數(shù)會(huì)優(yōu)先執(zhí)行輸出setTimeout 100
延遲1000
毫秒的回調(diào)函數(shù)會(huì)后執(zhí)行輸出setTimeout 1000
上面的例子比較簡(jiǎn)單,相信只要你看懂了上面筆者說(shuō)的同步異步任務(wù)做出來(lái)是沒(méi)什么問(wèn)題的。那下面筆者再舉一個(gè)例子小伙伴們看看會(huì)輸出啥呢?
function test2() { console.log("log1"); setTimeout(() => { console.log("setTimeout 1000"); }, 1000); setTimeout(() => { console.log("setTimeout 100"); }, 100); new Promise((resolve, reject) => { console.log("new promise"); resolve(); }).then(() => { console.log("promise.then"); }); console.log("log2"); } test2();
要解決上面的問(wèn)題光知道同步和異步任務(wù)是不夠的,我們還得知道宏任務(wù)和微任務(wù)。
在js
中,任務(wù)被分為兩種,一種叫宏任務(wù)MacroTask
,一種叫微任務(wù)MicroTask
。
常見(jiàn)的宏任務(wù)MacroTask
有
主代碼塊
setTimeout()
setInterval()
setImmediate() - Node
requestAnimationFrame() - 瀏覽器
常見(jiàn)的微任務(wù)MicroTask
有
Promise.then()
process.nextTick() - Node
所以在上面的例子中就涉及到宏任務(wù)和微任務(wù)了,那宏任務(wù)微任務(wù)的執(zhí)行順序是怎么樣的呢?
首先,整體的 script
(作為第一個(gè)宏任務(wù))開(kāi)始執(zhí)行的時(shí)候,會(huì)把所有代碼分為同步任務(wù)、異步任務(wù)兩部分,同步任務(wù)會(huì)直接進(jìn)入主線程依次執(zhí)行,異步任務(wù)會(huì)進(jìn)入異步隊(duì)列然后再分為宏任務(wù)和微任務(wù)。
宏任務(wù)進(jìn)入到 Event Table
中,并在里面注冊(cè)回調(diào)函數(shù),每當(dāng)指定的事件完成時(shí),Event Table
會(huì)將這個(gè)函數(shù)移到 Event Queue
中
微任務(wù)也會(huì)進(jìn)入到另一個(gè) Event Table
中,并在里面注冊(cè)回調(diào)函數(shù),每當(dāng)指定的事件完成時(shí),Event Table
會(huì)將這個(gè)函數(shù)移到 Event Queue
中
當(dāng)主線程內(nèi)的任務(wù)執(zhí)行完畢,主線程為空時(shí),會(huì)檢查微任務(wù)的 Event Queue
,如果有任務(wù),就全部執(zhí)行,如果沒(méi)有就執(zhí)行下一個(gè)宏任務(wù)
我們用一張圖來(lái)總結(jié)下
讀懂了異步里面的宏任務(wù)和微任務(wù)上面的例子我們就可以輕易的得到答案了。
我們知道在js中會(huì)優(yōu)先執(zhí)行同步任務(wù)再執(zhí)行異步任務(wù),所以上面的例子會(huì)先輸出log1、new promise、log2
。這里需要注意new promise里面是同步的
主代碼塊作為宏任務(wù)執(zhí)行完后會(huì)執(zhí)行此宏任務(wù)所產(chǎn)生的所有微任務(wù),所以會(huì)輸出promise.then
所有微任務(wù)執(zhí)行完畢后會(huì)再執(zhí)行一個(gè)宏任務(wù),延遲100
毫秒的回調(diào)函數(shù)會(huì)優(yōu)先執(zhí)行輸出setTimeout 100
此宏任務(wù)沒(méi)有產(chǎn)生微任務(wù),所以沒(méi)有微任務(wù)需要執(zhí)行
繼續(xù)執(zhí)行下一個(gè)宏任務(wù),延遲1000
毫秒的回調(diào)函數(shù)會(huì)優(yōu)執(zhí)行輸出setTimeout 1000
所以test2方法執(zhí)行后會(huì)依次輸出log1、new promise、log2、promise.then、setTimeout 100、setTimeout 1000
關(guān)于
js
執(zhí)行到底是先宏任務(wù)再微任務(wù)還是先微任務(wù)再宏任務(wù)網(wǎng)上的文章各有說(shuō)辭。筆者的理解是如果把整個(gè)js
代碼塊當(dāng)做宏任務(wù)的時(shí)候我們的js
執(zhí)行順序是先宏任務(wù)后微任務(wù)的。
正所謂百看不如一練,下面筆者舉兩個(gè)例子如果你都能做對(duì)那你算是掌握了js
執(zhí)行機(jī)制這一塊的知識(shí)了。
例子1
function test3() { console.log(1); setTimeout(function () { console.log(2); new Promise(function (resolve) { console.log(3); resolve(); }).then(function () { console.log(4); }); console.log(5); }, 1000); new Promise(function (resolve) { console.log(6); resolve(); }).then(function () { console.log(7); setTimeout(function () { console.log(8); }); }); setTimeout(function () { console.log(9); new Promise(function (resolve) { console.log(10); resolve(); }).then(function () { console.log(11); }); }, 100); console.log(12); } test3();
我們來(lái)具體分析下
首先js
整體代碼塊作為一個(gè)宏任務(wù)最開(kāi)始執(zhí)行,依次輸出1、6、12
。
整體代碼塊宏任務(wù)執(zhí)行完畢后產(chǎn)生了一個(gè)微任務(wù)和兩個(gè)宏任務(wù),所以宏任務(wù)隊(duì)列有兩個(gè)宏任務(wù),微任務(wù)隊(duì)列有一個(gè)微任務(wù)。
宏任務(wù)執(zhí)行完畢后會(huì)執(zhí)行此宏任務(wù)所產(chǎn)生的的所有微任務(wù)。因?yàn)橹挥幸粋€(gè)微任務(wù),所以會(huì)輸出7
。此微任務(wù)又產(chǎn)生了一個(gè)宏任務(wù),所以宏任務(wù)隊(duì)列目前有三個(gè)宏任務(wù)。
三個(gè)宏任務(wù)里面沒(méi)有設(shè)置延遲的最先執(zhí)行,所以輸出8
,此宏任務(wù)沒(méi)有產(chǎn)生微任務(wù),所以沒(méi)有微任務(wù)要執(zhí)行,繼續(xù)執(zhí)行下一個(gè)宏任務(wù)。
延遲100
毫秒的宏任務(wù)執(zhí)行,輸出9、10
,并產(chǎn)生了一個(gè)微任務(wù),所以微任務(wù)隊(duì)列目前有一個(gè)微任務(wù)
宏任務(wù)執(zhí)行完畢后會(huì)執(zhí)行該宏任務(wù)所產(chǎn)生的所有微任務(wù),所以會(huì)執(zhí)行微任務(wù)隊(duì)列的所有微任務(wù),輸出11
延遲1000
毫秒的宏任務(wù)執(zhí)行輸出2、3、5
,并產(chǎn)生了一個(gè)微任務(wù),所以微任務(wù)隊(duì)列目前有一個(gè)微任務(wù)
宏任務(wù)執(zhí)行完畢后會(huì)執(zhí)行該宏任務(wù)所產(chǎn)生的所有微任務(wù),所以會(huì)執(zhí)行微任務(wù)隊(duì)列的所有微任務(wù),輸出4
所以上面代碼例子會(huì)依次輸出1、6、12、7、8、9、10、11、2、3、5、4
,小伙伴們是否做對(duì)了呢?
例子2
我們把上面的例子1稍作修改,引入async
和await
async function test4() { console.log(1); setTimeout(function () { console.log(2); new Promise(function (resolve) { console.log(3); resolve(); }).then(function () { console.log(4); }); console.log(5); }, 1000); new Promise(function (resolve) { console.log(6); resolve(); }).then(function () { console.log(7); setTimeout(function () { console.log(8); }); }); const result = await async1(); console.log(result); setTimeout(function () { console.log(9); new Promise(function (resolve) { console.log(10); resolve(); }).then(function () { console.log(11); }); }, 100); console.log(12); } async function async1() { console.log(13) return Promise.resolve("Promise.resolve"); } test4();
上面這里例子會(huì)輸出什么呢?這里我們弄懂async
和await
題目就迎刃而解了。
我們知道async
和await
其實(shí)是Promise
的語(yǔ)法糖,這里我們只需要知道await
后面就相當(dāng)于Promise.then
。所以上面的例子我們可以理解成如下代碼
function test4() { console.log(1); setTimeout(function () { console.log(2); new Promise(function (resolve) { console.log(3); resolve(); }).then(function () { console.log(4); }); console.log(5); }, 1000); new Promise(function (resolve) { console.log(6); resolve(); }).then(function () { console.log(7); setTimeout(function () { console.log(8); }); }); new Promise(function (resolve) { console.log(13); return resolve("Promise.resolve"); }).then((result) => { console.log(result); setTimeout(function () { console.log(9); new Promise(function (resolve) { console.log(10); resolve(); }).then(function () { console.log(11); }); }, 100); console.log(12); }); } test4();
看到上面的代碼是不是就能輕易得出結(jié)果呢?
首先js
整體代碼塊作為一個(gè)宏任務(wù)最開(kāi)始執(zhí)行,依次輸出1、6、13
。
整體代碼塊宏任務(wù)執(zhí)行完畢后產(chǎn)生了兩個(gè)微任務(wù)和一個(gè)宏任務(wù),所以宏任務(wù)隊(duì)列有一個(gè)宏任務(wù),微任務(wù)隊(duì)列有兩個(gè)微任務(wù)。
宏任務(wù)執(zhí)行完畢后會(huì)執(zhí)行此宏任務(wù)所產(chǎn)生的的所有微任務(wù)。所以會(huì)輸出7、Promise.resolve、12
。此微任務(wù)又產(chǎn)生了兩個(gè)宏任務(wù),所以宏任務(wù)隊(duì)列目前有三個(gè)宏任務(wù)。
三個(gè)宏任務(wù)里面沒(méi)有設(shè)置延遲的最先執(zhí)行,所以輸出8
,此宏任務(wù)沒(méi)有產(chǎn)生微任務(wù),所以沒(méi)有微任務(wù)要執(zhí)行,繼續(xù)執(zhí)行下一個(gè)宏任務(wù)。
延遲100
毫秒的宏任務(wù)執(zhí)行,輸出9、10
,并產(chǎn)生了一個(gè)微任務(wù),所以微任務(wù)隊(duì)列目前有一個(gè)微任務(wù)
宏任務(wù)執(zhí)行完畢后會(huì)執(zhí)行該宏任務(wù)所產(chǎn)生的所有微任務(wù),所以會(huì)執(zhí)行微任務(wù)隊(duì)列的所有微任務(wù),輸出11
延遲1000
毫秒的宏任務(wù)執(zhí)行輸出2、3、5
,并產(chǎn)生了一個(gè)微任務(wù),所以微任務(wù)隊(duì)列目前有一個(gè)微任務(wù)
宏任務(wù)執(zhí)行完畢后會(huì)執(zhí)行該宏任務(wù)所產(chǎn)生的所有微任務(wù),所以會(huì)執(zhí)行微任務(wù)隊(duì)列的所有微任務(wù),輸出4
所以上面代碼例子會(huì)依次輸出1、6、13、7、Promise.resolve、12、8、9、10、11、2、3、5、4
,小伙伴們是否做對(duì)了呢?
關(guān)于setTimeout(fn)
可能很多小伙伴還是不太理解,這不明明沒(méi)設(shè)置延遲時(shí)間嗎,不應(yīng)該立即就執(zhí)行嗎?
setTimeout(fn)
我們可以理解成setTimeout(fn,0)
,其實(shí)是同一個(gè)意思。
我們知道js分同步任務(wù)和異步任務(wù),setTimeout(fn)
就是屬于異步任務(wù),所以這里就算你沒(méi)設(shè)置延遲時(shí)間,他也會(huì)進(jìn)入異步隊(duì)列,需要等到主線程空閑的時(shí)候才會(huì)執(zhí)行。
筆者這里再提一嘴,你覺(jué)得我們?cè)?code>setTimeout后面設(shè)置的延遲時(shí)間,js
就一定會(huì)按我們的延遲時(shí)間執(zhí)行嗎,我覺(jué)得并不見(jiàn)得。我們?cè)O(shè)置的時(shí)間只是該回調(diào)函數(shù)可以被執(zhí)行了,但是主線程有沒(méi)有空還是另外一回事,我們可以舉個(gè)簡(jiǎn)單的例子。
function test5() { setTimeout(function () { console.log("setTimeout"); }, 100); let i = 0; while (true) { i++; } } test5();
上面的例子一定會(huì)在100
毫秒后輸出setTimeout
嗎,并不會(huì),因?yàn)槲覀兊闹骶€程進(jìn)入了死循環(huán),并沒(méi)有空去執(zhí)行異步隊(duì)列的任務(wù)。
GUI渲染
在這里說(shuō)有些小伙伴可能不太理解,后面筆者會(huì)出關(guān)于瀏覽器的文章會(huì)再詳細(xì)介紹,這里只是簡(jiǎn)單了解下即可。
由于JS引擎線程
和GUI渲染線程
是互斥的關(guān)系,瀏覽器為了能夠使宏任務(wù)
和DOM任務(wù)
有序的進(jìn)行,會(huì)在一個(gè)宏任務(wù)
執(zhí)行結(jié)果后,在下一個(gè)宏任務(wù)
執(zhí)行前,GUI渲染線程
開(kāi)始工作,對(duì)頁(yè)面進(jìn)行渲染。
所以宏任務(wù)、微任務(wù)、GUI渲染之間的關(guān)系如下
宏任務(wù) -> 微任務(wù) -> GUI渲染 -> 宏任務(wù) -> ...
以上是“JavaScript中執(zhí)行上下文和執(zhí)行機(jī)制的示例分析”這篇文章的所有內(nèi)容,感謝各位的閱讀!希望分享的內(nèi)容對(duì)大家有幫助,更多相關(guān)知識(shí),歡迎關(guān)注億速云行業(yè)資訊頻道!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。