js是單線程為什么可以進(jìn)行異步操作?

(1) JS是單線程語(yǔ)言

(2) JS的Event Loop是JS的執(zhí)行機(jī)制。深入了解JS的執(zhí)行,就等于深入了解JS里的event loop。

1. JS為什么是單線程的? 為什么需要異步? 單線程又是如何實(shí)現(xiàn)異步的呢?

問(wèn)題1:JS為什么是單線程的?

? ? ?JS最初被設(shè)計(jì)用在瀏覽器中,那么想象一下,如果瀏覽器中的JS是多線程的。

那么現(xiàn)在有2個(gè)進(jìn)程,process1?process2,由于是多進(jìn)程的JS,所以他們對(duì)同一個(gè)dom,同時(shí)進(jìn)行操作

process1?刪除了該dom,而process2?編輯了該dom,同時(shí)下達(dá)2個(gè)矛盾的命令,瀏覽器究竟該如何執(zhí)行呢?

問(wèn)題2:JS為什么需要異步?

? ? ?如果JS中不存在異步,只能自上而下執(zhí)行,如果上一行解析時(shí)間很長(zhǎng),那么下面的代碼就會(huì)被阻塞。對(duì)于用戶而言,阻塞就意味著"卡死",這樣就導(dǎo)致了很差的用戶體驗(yàn),所以,JS中存在異步執(zhí)行。

問(wèn)題3:JS單線程又是如何實(shí)現(xiàn)異步的呢?

既然JS是單線程的,只能在一條線程上執(zhí)行,又是如何實(shí)現(xiàn)的異步呢?

是通過(guò)的事件循環(huán)(event loop),理解了event loop機(jī)制,就理解了JS的執(zhí)行機(jī)制。

-------------------------------------------------------------------------------------------------------------------------------------------------

JS中的event loop(1)

例1,觀察它的執(zhí)行順序

console.log(1)

setTimeout(function(){

console.log(2)

},0)

console.log(3)

運(yùn)行結(jié)果是: 1 3 2

也就是說(shuō),setTimeout里的函數(shù)并沒(méi)有立即執(zhí)行,而是延遲了一段時(shí)間,滿足一定條件后,才去執(zhí)行的,這類(lèi)代碼,我們叫異步代碼。

所以,這里我們首先知道了JS里的一種分類(lèi)方式,就是將任務(wù)分為: 同步任務(wù)和異步任務(wù)

圖片描述

按照這種分類(lèi)方式:JS的執(zhí)行機(jī)制是

首先判斷JS是同步還是異步,同步就進(jìn)入主進(jìn)程,異步就進(jìn)入event table

異步任務(wù)在event table中注冊(cè)函數(shù),當(dāng)滿足觸發(fā)條件后,被推入event queue

同步任務(wù)進(jìn)入主線程后一直執(zhí)行,直到主線程空閑時(shí),才會(huì)去event queue中查看是否有可執(zhí)行的異步任務(wù),如果有就推入主進(jìn)程中

以上三步循環(huán)執(zhí)行,這就是event loop。

? ??console.log(1)?是同步任務(wù),放入主線程里

setTimeout()?是異步任務(wù),被放入event?table,?0秒之后被推入event?queue里

console.log(3 )是同步任務(wù),放到主線程里

當(dāng)?1、?3在控制條被打印后,主線程去event?queue(事件隊(duì)列)里查看是否有可執(zhí)行的函數(shù),執(zhí)行setTimeout里的函數(shù)

------------------------------------------------------------------------------------------------------------------------------------------

JS中的event loop(2)

setTimeout(function(){

?console.log('定時(shí)器開(kāi)始啦')

});

new?Promise(function(resolve){

?console.log('馬上執(zhí)行for循環(huán)啦');

?for(var?i?=?0;?i

嘗試按照,上文我們剛學(xué)到的JS執(zhí)行機(jī)制去分析

setTimeout?是異步任務(wù),被放到event?table

new?Promise?是同步任務(wù),被放到主進(jìn)程里,直接執(zhí)行打印?console.log('馬上執(zhí)行for循環(huán)啦')

.then里的函數(shù)是?異步任務(wù),被放到event?table

console.log('代碼執(zhí)行結(jié)束')是同步代碼,被放到主進(jìn)程里,直接執(zhí)行

---------------------------------------------------------------------------------------------------------------------

所以,結(jié)果是 【馬上執(zhí)行for循環(huán)啦 — 代碼執(zhí)行結(jié)束 — 定時(shí)器開(kāi)始啦 — 執(zhí)行then函數(shù)啦】嗎?

親自執(zhí)行后,結(jié)果居然不是這樣,而是【馬上執(zhí)行for循環(huán)啦 — 代碼執(zhí)行結(jié)束 — 執(zhí)行then函數(shù)啦 — 定時(shí)器開(kāi)始啦】

那么,難道是異步任務(wù)的執(zhí)行順序,不是前后順序,而是另有規(guī)定? 事實(shí)上,按照異步和同步的劃分方式,并不準(zhǔn)確。

而準(zhǔn)確的劃分方式是:

macro-task(宏任務(wù)):包括整體代碼script,setTimeout,setInterval

micro-task(微任務(wù)):Promise,process.nextTick.

按照這種分類(lèi)方式:JS的執(zhí)行機(jī)制是:

執(zhí)行一個(gè)宏任務(wù),過(guò)程中如果遇到微任務(wù),就將其放到微任務(wù)的【事件隊(duì)列】里

當(dāng)前宏任務(wù)執(zhí)行完成后,會(huì)查看微任務(wù)的【事件隊(duì)列】,并將里面全部的微任務(wù)依次執(zhí)行完

重復(fù)以上2步驟,結(jié)合event loop(1) event loop(2) ,就是更為準(zhǔn)確的JS執(zhí)行機(jī)制了。

---------------------------------------------------------------------------------------------------------------------

首先執(zhí)行script下的宏任務(wù),遇到setTimeout,將其放到宏任務(wù)的【隊(duì)列】里

遇到?new?Promise直接執(zhí)行,打印"馬上執(zhí)行for循環(huán)啦"

遇到then方法,是微任務(wù),將其放到微任務(wù)的【隊(duì)列里】

打印?"代碼執(zhí)行結(jié)束"

本輪宏任務(wù)執(zhí)行完畢,查看本輪的微任務(wù),發(fā)現(xiàn)有一個(gè)then方法里的函數(shù),?打印"執(zhí)行then函數(shù)啦"

到此,本輪的event?loop?全部完成。下一輪的循環(huán)里,先執(zhí)行一個(gè)宏任務(wù),發(fā)現(xiàn)宏任務(wù)的【隊(duì)列】里有一個(gè)?setTimeout里的函數(shù),執(zhí)行打印"定時(shí)器開(kāi)始啦"

所以最后的執(zhí)行順序是【馬上執(zhí)行for循環(huán)啦 — 代碼執(zhí)行結(jié)束 — 執(zhí)行then函數(shù)啦 — 定時(shí)器開(kāi)始啦】

-------------------------------------------------------------------------------------------------------------------------------------

?談?wù)剆etTimeout

這段setTimeout代碼什么意思? 我們一般說(shuō): 3秒后,會(huì)執(zhí)行setTimeout里的那個(gè)函數(shù)

setTimeout(function(){

console.log('執(zhí)行了')

},3000)

但是這種說(shuō)并不嚴(yán)謹(jǐn),準(zhǔn)確的解釋是: 3秒后,setTimeout里的函數(shù)被會(huì)推入event queue,而event queue(事件隊(duì)列)里的任務(wù),只有在主線程空閑時(shí)才會(huì)執(zhí)行。

所以只有滿足 (1)3秒后 (2)主線程空閑,同時(shí)滿足時(shí),才會(huì)3秒后執(zhí)行該函數(shù)

如果主線程執(zhí)行內(nèi)容很多,執(zhí)行時(shí)間超過(guò)3秒,比如執(zhí)行了10秒,那么這個(gè)函數(shù)只能10秒后執(zhí)行了

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容