這篇文章主要講解了“如何理解JS的同步異步編程和EventLoop底層機(jī)制”,文中的講解內(nèi)容簡(jiǎn)單清晰,易于學(xué)習(xí)與理解,下面請(qǐng)大家跟著小編的思路慢慢深入,一起來(lái)研究和學(xué)習(xí)“如何理解JS的同步異步編程和EventLoop底層機(jī)制”吧!
創(chuàng)新互聯(lián)一直秉承“誠(chéng)信做人,踏實(shí)做事”的原則,不欺瞞客戶,是我們最起碼的底線! 以服務(wù)為基礎(chǔ),以質(zhì)量求生存,以技術(shù)求發(fā)展,成交一個(gè)客戶多一個(gè)朋友!為您提供做網(wǎng)站、網(wǎng)站制作、成都網(wǎng)頁(yè)設(shè)計(jì)、微信平臺(tái)小程序開(kāi)發(fā)、成都網(wǎng)站開(kāi)發(fā)、成都網(wǎng)站制作、成都軟件開(kāi)發(fā)、App定制開(kāi)發(fā)是成都本地專業(yè)的網(wǎng)站建設(shè)和網(wǎng)站設(shè)計(jì)公司,等你一起來(lái)見(jiàn)證!
1)JS是單線程的,大部分代碼都是同步編程。
2)JS中利用瀏覽器的多線程機(jī)制,基于單線程的EventLoop(事件循環(huán)機(jī)制)實(shí)現(xiàn)出異步的效果。
1)微任務(wù) (microtask):優(yōu)先級(jí)高,并且可以插隊(duì),不是先定義先執(zhí)行。包括:promise.then,async/await [generator],requestAnimationFrame,observer,MutationObserver,setImmediate。
2)宏任務(wù) (macrotask):優(yōu)先級(jí)低,先定義的先執(zhí)行。包括:ajax,setTimeout,setInterval,事件綁定,postMessage,MessageChannel(用于消息通訊)。
+先找微任務(wù)隊(duì)列,如果微任務(wù)隊(duì)列中有,先從微任務(wù)隊(duì)列中,一般按照存放順序獲取并且去執(zhí)行。
+如果微任務(wù)隊(duì)列中沒(méi)有,則再去宏任務(wù)隊(duì)列中查找,在宏任務(wù)隊(duì)列中,一般是按照誰(shuí)先到達(dá)執(zhí)行的條件,就先把誰(shuí)拿出來(lái)執(zhí)行。
? 面試題1:
console.log('1') // 1 async function async1() { console.log('2') // 2 await setTimeout(() => { console.log('3') // 8 }, 0) console.log('4') // 5 } setTimeout(() => { console.log('5') // 7 }, 0) async1() new Promise(function (resolve) { console.log('6') // 3 resolve() }).then(function () { console.log('7') // 6 }) console.log('8') // 4 // 結(jié)果:1 2 6 8 4 7 5 3
? 面試題2:
async function async1() { console.log('async1 start'); // 2 await async2(); console.log('async1 end'); // 6 } async function async2() { console.log('async2'); // 3 } console.log('script start'); // 1 setTimeout(function () { console.log('setTimeout'); // 8 }, 0) async1(); new Promise(function (resolve) { console.log('promise1'); // 4 resolve(); }).then(function () { console.log('promise2'); // 7 }); console.log('script end'); // 5 //結(jié)果: script start async1 start async2 promise1 script end async1 end promise2 setTimeout
? 面試題3:
console.log(1); // 1 setTimeout(() => { console.log(2); // 6 Promise.resolve().then(data => { console.log(3); // 7 }); }); new Promise((resolve) => { resolve() console.log(4) // 2 }).then(() => { console.log(5); // 4 setTimeout(() => { console.log(6); // 8 }); }).then(() => console.log(7)) // 5 console.log(8); // 3 // 結(jié)果:1, 4, 8, 5, 7, 2, 3, 6 1, 4, 8 是同步 5, 7 是微任務(wù) 2 宏任務(wù) 3 微任務(wù) 6 宏任務(wù)
* 核心答案 | 基礎(chǔ)知識(shí)要夯實(shí)
1) 進(jìn)程代表的是一個(gè)程序(瀏覽器開(kāi)一個(gè)頁(yè)卡 (Tab頁(yè)) 就是一個(gè)進(jìn)程);
2) 線程是用來(lái)處理處理進(jìn)程中的具體事物的,如果一個(gè)程序中需要同時(shí)做好多事情,就需要開(kāi)辟好多線程;
3) 一個(gè)線程同時(shí)只能做一件事情;
官方的說(shuō)法
1) 進(jìn)程是cpu資源分配的最小單位(是能擁有資源和獨(dú)立運(yùn)行的最小單位)。
2) 線程是cpu調(diào)度的最小單位(線程是建立在進(jìn)程的基礎(chǔ)上的一次程序運(yùn)行單位,一個(gè)進(jìn)程中可以有多個(gè)線程)。
* 核心答案 | 基礎(chǔ)知識(shí)要夯實(shí)
1) 瀏覽器是多進(jìn)程的;
2) 瀏覽器之所以能夠運(yùn)行,是因?yàn)橄到y(tǒng)給它的進(jìn)程分配了資源(cpu、內(nèi)存);
3) 簡(jiǎn)單點(diǎn)理解,每打開(kāi)一個(gè)Tab頁(yè),就相當(dāng)于創(chuàng)建了一個(gè)獨(dú)立的瀏覽器進(jìn)程;
那么接下來(lái)看看它都包含了哪些線程(列舉一些主要常駐線程)
1) 負(fù)責(zé)渲染瀏覽器界面,解析HTML,CSS,構(gòu)建DOM樹(shù)和RenderObject樹(shù),布局和繪制等。
2) 當(dāng)界面需要重繪(Repaint)或由于某種操作引發(fā)回流(reflow)時(shí),該線程就會(huì)執(zhí)行。
3) 注意,GUI渲染線程與JS引擎線程是互斥的,當(dāng)JS引擎執(zhí)行時(shí)GUI線程會(huì)被掛起(相當(dāng)于被凍結(jié)了),GUI更新會(huì)被保存在一個(gè)隊(duì)列中等到JS引擎空閑時(shí)立即被執(zhí)行。
也稱為JS內(nèi)核,負(fù)責(zé)處理Javascript腳本程序。(例如V8引擎)
1) JS引擎線程負(fù)責(zé)解析Javascript腳本,運(yùn)行代碼。
2) JS引擎一直等待著任務(wù)隊(duì)列中任務(wù)的到來(lái),然后加以處理,一個(gè)Tab頁(yè)(renderer進(jìn)程)中無(wú)論什么時(shí)候都只有一個(gè)JS線程在運(yùn)行JS程序。
3) 同樣注意,GUI渲染線程與JS引擎線程是互斥的,所以如果JS執(zhí)行的時(shí)間過(guò)長(zhǎng),這樣就會(huì)造成頁(yè)面的渲染不連貫,導(dǎo)致頁(yè)面渲染加載阻塞。
* 核心答案 | 基礎(chǔ)知識(shí)要夯實(shí)
JS是單線程的:瀏覽器只分配一個(gè)線程用來(lái)渲染JS代碼。
1、JS中的代碼大部分都是“同步編程”:上面的任務(wù)沒(méi)有處理完成,下面的任務(wù)是無(wú)法處理的。
2?但是JS中利用瀏覽器的多線程機(jī)制,可以規(guī)劃出“異步編程”效果。
定時(shí)器
ajax/Fetch/跨域 (HTTP網(wǎng)絡(luò)請(qǐng)求)
事件綁定
Promise中有也有異步編程
Generator / yield
async / await
計(jì)算程序執(zhí)行的時(shí)間(預(yù)估)
1)運(yùn)行監(jiān)控 console.time/timeEnd(受當(dāng)前電腦運(yùn)行環(huán)境的影響)
2)大O表示法(提前預(yù)估)
console.time('AAA'); for (let i = 0; i < 99999999; i++) {} console.timeEnd('AAA');
真實(shí)項(xiàng)目中應(yīng)該避免死循環(huán) (重要)
while (true) {} console.log('OK'); // 不執(zhí)行:上述的死循環(huán)一直占用這“JS渲染線程”,線程空閑不下來(lái),就處理不了其他的事情
1)設(shè)置定時(shí)器任務(wù)是同步的
2)“間隔interval這么長(zhǎng)時(shí)間,執(zhí)行定時(shí)器綁定的函數(shù)” 這個(gè)任務(wù)是異步的
3)遇到異步任務(wù),瀏覽器不會(huì)等待它執(zhí)行完,則繼續(xù)渲染下面的代碼;當(dāng)?shù)鹊较旅娲a運(yùn)行完,時(shí)間也到達(dá)了執(zhí)行的條件,才會(huì)把異步任務(wù)執(zhí)行;
setTimeout(() => { console.log("OK"); //2 }, 1000); console.log('NO'); //1
interval設(shè)置為零也不是立即執(zhí)行,而是瀏覽器都有“最快反應(yīng)時(shí)間(谷歌:5~6ms IE:13~17ms)”,設(shè)置為零,最快也需要等到5~6ms左右
setTimeout(() => { console.log('OK'); //2 }, 0); console.log('NO'); //1
setTimeout(() => { console.log(1); }, 20); console.log(2); setTimeout(() => { console.log(3); }, 10); console.log(4); console.time('AA'); for (let i = 0; i < 90000000; i++) { // do soming } console.timeEnd('AA'); //=>AA: 79ms 左右 console.log(5); setTimeout(() => { console.log(6); }, 8); console.log(7); setTimeout(() => { console.log(8); }, 15); console.log(9); // 結(jié)果:2,4,5,7,9,3,1,6,8
畫圖分析:( 有圖有真相 )
執(zhí)行順序:同步任務(wù) —> 微任務(wù) —> 宏任務(wù) (微任務(wù)、宏任務(wù)在EventQueue)
細(xì)節(jié)點(diǎn)
當(dāng)棧中的“同步任務(wù)”或者其它任務(wù)沒(méi)有執(zhí)行完之前,JS渲染線程不會(huì)空閑下來(lái),些時(shí)哪怕定時(shí)器已經(jīng)到達(dá)指定時(shí)間,也不會(huì)執(zhí)行的。 “JS是單線程的,一次只能做一件事情” => 定時(shí)器設(shè)定的等待時(shí)間是最快觸發(fā)執(zhí)行的時(shí)間,很多時(shí)候,到時(shí)間不一定會(huì)執(zhí)行,只有JS渲染線程空閑下來(lái)才會(huì)執(zhí)行。
感謝各位的閱讀,以上就是“如何理解JS的同步異步編程和EventLoop底層機(jī)制”的內(nèi)容了,經(jīng)過(guò)本文的學(xué)習(xí)后,相信大家對(duì)如何理解JS的同步異步編程和EventLoop底層機(jī)制這一問(wèn)題有了更深刻的體會(huì),具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是創(chuàng)新互聯(lián),小編將為大家推送更多相關(guān)知識(shí)點(diǎn)的文章,歡迎關(guān)注!