您好,登錄后才能下訂單哦!
這篇“JavaScript單線程和異步怎么實(shí)現(xiàn)”文章的知識(shí)點(diǎn)大部分人都不太理解,所以小編給大家總結(jié)了以下內(nèi)容,內(nèi)容詳細(xì),步驟清晰,具有一定的借鑒價(jià)值,希望大家閱讀完這篇文章能有所收獲,下面我們一起來(lái)看看這篇“JavaScript單線程和異步怎么實(shí)現(xiàn)”文章吧。
1. 進(jìn)程:程序的一次執(zhí)行, 它占有一片獨(dú)有的內(nèi)存空間 ---- 可以通過(guò)windows任務(wù)管理器查看進(jìn)程;
2. 線程: 是進(jìn)程內(nèi)的一個(gè)獨(dú)立執(zhí)行單元;是程序執(zhí)行的一個(gè)完整流程;CPU的基本調(diào)度單位;
3. 進(jìn)程與線程的關(guān)系:
* 一個(gè)進(jìn)程中一般至少有一個(gè)運(yùn)行的線程: 主線程 -- 進(jìn)程啟動(dòng)后自動(dòng)創(chuàng)建;
* 一個(gè)進(jìn)程中也可以同時(shí)運(yùn)行多個(gè)線程, 我們會(huì)說(shuō)程序是多線程運(yùn)行的;
* 一個(gè)進(jìn)程內(nèi)的數(shù)據(jù)可以供其中的多個(gè)線程直接共享;
* 多個(gè)進(jìn)程之間的數(shù)據(jù)是不能直接共享的
4. 瀏覽器運(yùn)行是單進(jìn)程還是多進(jìn)程?
* 有的是單進(jìn)程
* firefox
* 有的是多進(jìn)程
* chrome
5. 如何查看瀏覽器是否是多進(jìn)程運(yùn)行的呢?
* 任務(wù)管理器==>進(jìn)程
6. 瀏覽器運(yùn)行是單線程還是多線程?
* 都是多線程運(yùn)行的
JavaScript語(yǔ)言的一大特點(diǎn)就是單線程,也就是說(shuō)同一時(shí)間只能做一件事。
//栗子 console.log(1) console.log(2) console.log(3) //輸出順序 1 2 3
首先是歷史原因,在創(chuàng)建 javascript 這門(mén)語(yǔ)言時(shí),多進(jìn)程、多線程的架構(gòu)并不流行,硬件支持并不好。
其次是因?yàn)槎嗑€程的復(fù)雜性,多線程操作需要加鎖,編碼的復(fù)雜性會(huì)增高。
最后與它的用途有關(guān),作為瀏覽器腳本語(yǔ)言,JavaScript的主要用途是與用戶互動(dòng),以及操作DOM,如果同時(shí)操作 DOM ,在多線程不加鎖的情況下,最終會(huì)導(dǎo)致 DOM 渲染的結(jié)果不可預(yù)期。
為了利用多核CPU的計(jì)算能力,HTML5提出Web Worker標(biāo)準(zhǔn),允許JavaScript腳本創(chuàng)建多個(gè)線程,但是子線程完全受主線程控制,且不得操作DOM。所以,這個(gè)新標(biāo)準(zhǔn)并沒(méi)有改變JavaScript單線程的本質(zhì)。
同步任務(wù): 在主線程上排隊(duì)執(zhí)行的任務(wù),只有前一個(gè)任務(wù)執(zhí)行完畢,才能執(zhí)行后一個(gè)任務(wù);
所有同步任務(wù)都在主線程上執(zhí)行,形成一個(gè)執(zhí)行棧(execution context stack)。
異步任務(wù):在主線程外執(zhí)行的任務(wù);在主線程之外還存在一個(gè)“任務(wù)隊(duì)列”(task queue),當(dāng)異步任務(wù)執(zhí)行完成后會(huì)以回調(diào)函數(shù)的方式放入任務(wù)隊(duì)列中等待,等主線程空閑時(shí),主線程就會(huì)去事件隊(duì)列中取出等待的回調(diào)函數(shù)放入主線程中進(jìn)行執(zhí)行。這個(gè)過(guò)程反復(fù)執(zhí)行就形成了js的事件循環(huán)機(jī)制(Event Loop)。
//栗子 // 同步 console.log(1) // 異步 setTimeout(()=>{ console.log(2) },100) // 同步 console.log(3) //輸出順序 1 3 2
如果在JS代碼執(zhí)行過(guò)程中,某段代碼執(zhí)行過(guò)久,后面的代碼遲遲不能執(zhí)行,產(chǎn)生阻塞(即卡死),會(huì)影響用戶體驗(yàn)。
其實(shí)上面我們已經(jīng)提到了,JS實(shí)現(xiàn)異步時(shí)通過(guò) 事件循環(huán);
我們先理解幾個(gè)概念:
JS任務(wù) 分為同步任務(wù)(synchronous)和異步任務(wù)(asynchronous)
同步任務(wù)都在 JS引擎線程(主線程) 上執(zhí)行,形成一個(gè)執(zhí)行棧(call stack)
事件觸發(fā)線程 管理一個(gè) 任務(wù)隊(duì)列(Task Queue)
異步任務(wù) 觸發(fā)條件達(dá)成,將 回調(diào)事件 放到任務(wù)隊(duì)列(Task Queue)中
執(zhí)行棧中所有同步任務(wù)執(zhí)行完畢,此時(shí)JS引擎線程空閑,系統(tǒng)會(huì)讀取任務(wù)隊(duì)列,將可運(yùn)行的異步任務(wù)回調(diào)事件添加到執(zhí)行棧中,開(kāi)始執(zhí)行
當(dāng)一個(gè)JS文件第一次執(zhí)行的時(shí)候,js引擎會(huì) 解析這段代碼,并將其中的同步代碼 按照?qǐng)?zhí)行順序加入執(zhí)行棧中,然后從頭開(kāi)始執(zhí)行。如果當(dāng)前執(zhí)行的是一個(gè)方法,那么js會(huì)向執(zhí)行棧中添加這個(gè)方法的執(zhí)行環(huán)境,然后進(jìn)入這個(gè)執(zhí)行環(huán)境繼續(xù)執(zhí)行其中的代碼。當(dāng)這個(gè)執(zhí)行環(huán)境中的代碼 執(zhí)行完畢并返回結(jié)果后,js會(huì)退出這個(gè)執(zhí)行環(huán)境并把這個(gè)執(zhí)行環(huán)境銷(xiāo)毀,回到上一個(gè)方法的執(zhí)行環(huán)境。這個(gè)過(guò)程反復(fù)進(jìn)行,直到執(zhí)行棧中的代碼全部執(zhí)行完畢。
栗子
//(1) console.log(1) //(2) setTimeout(()=>{ console.log(2) },100) //(3) console.log(3)
先解析整段代碼,按照順序加入到執(zhí)行棧中,從頭開(kāi)始執(zhí)行
先執(zhí)行(1),是同步的,所以直接打印 1
執(zhí)行(2),發(fā)現(xiàn)是 setTimeout,于是調(diào)用瀏覽器的方法(webApi)執(zhí)行,在 100ms后將 console.log(2) 加入到任務(wù)隊(duì)列
執(zhí)行(3),同步的,直接打印 3
執(zhí)行棧已經(jīng)清空了,現(xiàn)在檢查任務(wù)隊(duì)列,(執(zhí)行太快的話可能此時(shí)任務(wù)隊(duì)列還是空的,沒(méi)到100ms,還沒(méi)有將(2)的打印加到任務(wù)隊(duì)列,于是不停的檢測(cè),直到隊(duì)列中有任務(wù)),發(fā)現(xiàn)有 console.log(2),于是添加到執(zhí)行棧,執(zhí)行console.log(2),同步代碼,直接打印 2 (如果這里是異步任務(wù),同樣會(huì)再走一遍循環(huán):-->任務(wù)隊(duì)列->執(zhí)行棧)
所以結(jié)果是 1 3 2;
注意:setTimeout/Promise等我們稱(chēng)之為任務(wù)源。而進(jìn)入任務(wù)隊(duì)列的是他們指定的回調(diào);
上面的循環(huán)只是一個(gè)宏觀的表述,實(shí)際上異步任務(wù)之間也是有不同的,分為 宏任務(wù)(macro task) 與 微任務(wù)(micro task),最新的標(biāo)準(zhǔn)中,他們被稱(chēng)為 task與 jobs
宏任務(wù)有哪些:script(整體代碼), setTimeout, setInterval, setImmediate, I/O, UI rendering(渲染)
微任務(wù)有哪些:process.nextTick, Promise, Object.observe(已廢棄), MutationObserver(html5新特性)
下面我們?cè)僭敿?xì)講解一下執(zhí)行過(guò)程:
執(zhí)行棧在執(zhí)行的時(shí)候,會(huì)把宏任務(wù)放在一個(gè)宏任務(wù)的任務(wù)隊(duì)列,把微任務(wù)放在一個(gè)微任務(wù)的任務(wù)隊(duì)列,在當(dāng)前執(zhí)行棧為空的時(shí)候,主線程會(huì) 查看微任務(wù)隊(duì)列是否有事件存在。如果微任務(wù)隊(duì)列不存在,那么會(huì)去宏任務(wù)隊(duì)列中 取出一個(gè)任務(wù) 加入當(dāng)前執(zhí)行棧;如果微任務(wù)隊(duì)列存在,則會(huì)依次執(zhí)行微任務(wù)隊(duì)列中的所有任務(wù),直到微任務(wù)隊(duì)列為空(同樣,是吧隊(duì)列中的事件加到執(zhí)行棧執(zhí)行),然后去宏任務(wù)隊(duì)列中取出最前面的一個(gè)事件加入當(dāng)前執(zhí)行棧...如此反復(fù),進(jìn)入循環(huán)。
注意:
宏任務(wù)和微任務(wù)的任務(wù)隊(duì)列都可以有多個(gè)
當(dāng)前執(zhí)行棧執(zhí)行完畢時(shí)會(huì)立刻先處理所有微任務(wù)隊(duì)列中的事件,然后再去宏任務(wù)隊(duì)列中取出一個(gè)事件。同一次事件循環(huán)中,微任務(wù)永遠(yuǎn)在宏任務(wù)之前執(zhí)行。
不同的運(yùn)行環(huán)境 循環(huán)策略可能有不同,這里探討chrome、node環(huán)境
栗子
//(1) setTimeout(()=>{ console.log(1) // 宏任務(wù) },100) //(2) setTimeout(()=>{ console.log(2) // 宏任務(wù) },100) //(3) new Promise(function(resolve,reject){ //(4) console.log(3) // 直接打印 resolve(4) }).then(function(val){ //(5) console.log(val); // 微任務(wù) }) //(6) new Promise(function(resolve,reject){ //(7) console.log(5) // 直接打印 resolve(6) }).then(function(val){ //(8) console.log(val); // 微任務(wù) }) //(9) console.log(7) // 直接打印 //(10) setTimeout(()=>{ console.log(8) // 宏任務(wù),單比(1)(2)宏任務(wù)早 },50)
上面的代碼在node和chrome環(huán)境的正確打印順序是 3 5 7 4 6 8 1 2
下面分析一下執(zhí)行過(guò)程:
全部代碼在解析后加入執(zhí)行棧
執(zhí)行(1),宏任務(wù),調(diào)用webapi setTimeout,這個(gè)方法會(huì)在100ms后將回調(diào)函數(shù)放入宏任務(wù)的任務(wù)隊(duì)列
執(zhí)行(2),同(1),但是會(huì)比(1)稍后一點(diǎn)
執(zhí)行(3),同步執(zhí)行new Promise,然后執(zhí)行(4),直接打印 3 ,然后resolve(4),然后.then(),把(5)放入微任務(wù)的任務(wù)隊(duì)列
執(zhí)行(6),同上,先打印 5 ,再執(zhí)行resolve(6),然后.then()里面的內(nèi)容(8)加入到微任務(wù)的任務(wù)隊(duì)列
執(zhí)行(9),同步代碼,直接打印 7
執(zhí)行(10),同(1)和(2),只是時(shí)間更短,會(huì)在 50ms 后將回調(diào) console.log(8) 加入宏任務(wù)的任務(wù)隊(duì)列
現(xiàn)在執(zhí)行棧清空了,開(kāi)始檢查微任務(wù)隊(duì)列,發(fā)現(xiàn)(5),加入到執(zhí)行棧執(zhí)行,是同步代碼,直接打印 4
任務(wù)隊(duì)列又執(zhí)行完了,又檢查微任務(wù)隊(duì)列,發(fā)現(xiàn)(8),打印 6
任務(wù)隊(duì)列又執(zhí)行完了,檢查微任務(wù)隊(duì)列,沒(méi)有任務(wù),再檢查宏任務(wù)隊(duì)列,此時(shí)如果超過(guò)了50ms的話,會(huì)發(fā)現(xiàn) console.log(8) 在宏任務(wù)隊(duì)列中,于是執(zhí)行 打印 8
依次打印 1 2
注:因?yàn)殇秩疽彩呛耆蝿?wù),需要在一次執(zhí)行棧執(zhí)行完后才會(huì)執(zhí)行渲染,所以如果執(zhí)行棧中同時(shí)有幾個(gè)同步的改變同一個(gè)樣式的代碼,在渲染時(shí)只會(huì)渲染最后一個(gè)。
以上就是關(guān)于“JavaScript單線程和異步怎么實(shí)現(xiàn)”這篇文章的內(nèi)容,相信大家都有了一定的了解,希望小編分享的內(nèi)容對(duì)大家有幫助,若想了解更多相關(guān)的知識(shí)內(nèi)容,請(qǐng)關(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)容。