摘要:為了最終確認(rèn),進(jìn)行最后一次驗(yàn)證,在第一個(gè)里面多加一層同步新加行新加行新加行新加行新加行新加行同步輸出結(jié)果如下同步同步確認(rèn)完畢,的確是一層一層的執(zhí)行。而是微任務(wù),是宏任務(wù)。
久經(jīng)前端開(kāi)發(fā)沙場(chǎng),會(huì)經(jīng)歷各式各樣的需求,處理這些需求時(shí)候,會(huì)使用各種各樣的api和功能,這里集中對(duì)setTimeout和Promise的異步功能進(jìn)行討論一下。
多帶帶使用的執(zhí)行模式這里就使用Promise作為例子,來(lái)探究一下多帶帶使用它,會(huì)有哪些注意點(diǎn)。
1.最初的試探
執(zhí)行代碼,Promise的基本使用:
let fn = () => { console.log(1) let a = new Promise((resolve, reject) => { console.log(2) resolve(3) }) console.log(4) return a } // 執(zhí)行 fn().then(data => console.log(data))
以上代碼,輸出結(jié)果為:
1 // 同步 2 // 同步 4 // 同步 3 // 異步
注意 new Promise() 是同步方法,resolve才是異步方法。
此外,上面的方法,可以有下面這種寫(xiě)法,效果等同,主要是把Promise精簡(jiǎn)了一下:
let fn = () => { console.log(1) console.log(2) let a = Promise.resolve(3) console.log(4) return a } // 執(zhí)行 fn().then(data => console.log(data))
因?yàn)楝F(xiàn)在討論的是Promise的異步功能,所以下面均使用第二種寫(xiě)法的Promise
2.多個(gè)同級(jí)Promise
編輯器中,輸入以下代碼,多個(gè)同級(jí)的單層的Promise:
console.log("同步-0.1") Promise.resolve().then(() => { console.log("P-1.1") }) Promise.resolve().then(() => { console.log("P-1.2") }) Promise.resolve().then(() => { console.log("P-1.3") }) console.log("同步-0.2")
則會(huì)依次輸出以下打印,毫無(wú)疑問(wèn)的結(jié)果:
同步-0.1 同步-0.2 P-1.1 P-1.2 P-1.3
3.Promise套Promise
復(fù)雜一下,新增行有注釋說(shuō)明:
console.log("同步-0.1") Promise.resolve().then(() => { console.log("P-1.1") Promise.resolve().then(() => { // 新加行 console.log("P-2.1") // 新加行 }) // 新加行 }) Promise.resolve().then(() => { console.log("P-1.2") Promise.resolve().then(() => { // 新加行 console.log("P-2.2") // 新加行 }) // 新加行 }) Promise.resolve().then(() => { console.log("P-1.3") Promise.resolve().then(() => { // 新加行 console.log("P-2.3") // 新加行 }) // 新加行 }) console.log("同步-0.2")
輸出結(jié)果如下:
同步-0.1 同步-0.2 P-1.1 P-1.2 P-1.3 P-2.1 P-2.2 P-2.3
可見(jiàn),多層Promise是一層一層執(zhí)行的。
4.為了最終確認(rèn),進(jìn)行最后一次驗(yàn)證,在第一個(gè)Promise里面多加一層:
console.log("同步-0.1") Promise.resolve().then(() => { console.log("P-1.1") Promise.resolve().then(() => { console.log("P-2.1") Promise.resolve().then(() => { // 新加行 console.log("P-3.1") // 新加行 }) // 新加行 Promise.resolve().then(() => { // 新加行 console.log("P-3.2") // 新加行 }) // 新加行 }) }) Promise.resolve().then(() => { console.log("P-1.2") Promise.resolve().then(() => { console.log("P-2.2") }) }) Promise.resolve().then(() => { console.log("P-1.3") Promise.resolve().then(() => { console.log("P-2.3") }) }) console.log("同步-0.2")
輸出結(jié)果如下:
同步-0.1 同步-0.2 P-1.1 P-1.2 P-1.3 P-2.1 P-2.2 P-2.3 P-3.1 P-3.2
確認(rèn)完畢,的確是一層一層的執(zhí)行。
而且這里可以告訴大家,setTimeout和setInterval在多帶帶使用的時(shí)候,和Promise是一樣的,同樣是分層執(zhí)行,這里不再貼代碼了(友情提醒:setInterval的話,需要第一次執(zhí)行就把這個(gè)定時(shí)器清掉,否則就無(wú)限執(zhí)行,卡死頁(yè)面秒秒鐘的事兒),
混合使用的執(zhí)行模式接下來(lái)才是重點(diǎn),下面將setTimeout和Promise進(jìn)行混合操作。
console.log("同步-0.1") Promise.resolve().then(() => { console.log("P-1.1") }) setTimeout(() => { console.log("S-1.1") }); Promise.resolve().then(() => { console.log("P-1.2") }) setTimeout(() => { console.log("S-1.2") }); console.log("同步-0.2")
執(zhí)行結(jié)果如下。。。問(wèn)題暴露出來(lái)了:
同步-0.1 同步-0.2 P-1.1 P-1.2 S-1.1 S-1.2
為什么,在同級(jí)情況下,是Promise執(zhí)行完了setTimeout才會(huì)執(zhí)行?
是人性的泯滅,還是道德的淪喪?
是因?yàn)镴avaScript任務(wù)類型!
JavaScript的微任務(wù)和宏任務(wù)
敲黑板,標(biāo)重點(diǎn)。
JavaScript的任務(wù)分為微任務(wù)(Microtasks)和宏任務(wù)(task);
宏任務(wù)是主流,當(dāng)js開(kāi)始被執(zhí)行的時(shí)候,就是開(kāi)啟一個(gè)宏任務(wù),在宏任務(wù)中執(zhí)行一條一條的指令;
宏任務(wù)可以同時(shí)有多個(gè),但會(huì)按順序一個(gè)一個(gè)執(zhí)行;
每一個(gè)宏任務(wù),后面都可以跟一個(gè)微任務(wù)隊(duì)列,如果微任務(wù)隊(duì)列中有指令或方法,那么就會(huì)執(zhí)行;如果沒(méi)有,則開(kāi)始執(zhí)行下一個(gè)宏任務(wù),直到所有的宏任務(wù)執(zhí)行完為止,微任務(wù)相當(dāng)于宏任務(wù)的小尾巴;
為什么有了宏任務(wù),還會(huì)有微任務(wù)存在?因?yàn)楹耆蝿?wù)太占用性能,當(dāng)需要一些較早就準(zhǔn)備好的方法,排在最后才執(zhí)行的時(shí)候,又不想新增一個(gè)宏任務(wù),那么就可以把這些方法,一個(gè)一個(gè)的放在微任務(wù)隊(duì)列里面,在這個(gè)宏任務(wù)中的代碼執(zhí)行完后,就會(huì)執(zhí)行微任務(wù)隊(duì)列。
而Promise是微任務(wù),setTimeout是宏任務(wù)。
所以上面的代碼中,代碼執(zhí)行時(shí)會(huì)是如下場(chǎng)景:
開(kāi)始執(zhí)行當(dāng)前宏任務(wù)代碼!遇到了Promise?好嘞,把它里面的異步代碼,放在當(dāng)前這個(gè)宏任務(wù)后面微任務(wù)里面,然后繼續(xù)執(zhí)行咱的;
咦,有個(gè)setTimeout?是個(gè)宏任務(wù),那在當(dāng)前這個(gè)宏任務(wù)后面,創(chuàng)建第二個(gè)宏任務(wù),然后把這個(gè)setTimeout里面的代碼塞進(jìn)去,咱繼續(xù)執(zhí)行;
咦,又一個(gè)Promise?把他塞進(jìn)后面的微任務(wù)里。。。什么?已經(jīng)有代碼了?那有啥關(guān)系,繼續(xù)往里塞,放在已有代碼的后面就行,咱繼續(xù)執(zhí)行;
天啊,又來(lái)一個(gè)setTimeout,現(xiàn)在后面已經(jīng)有第二個(gè)宏任務(wù)了對(duì)吧?那就創(chuàng)建第三個(gè)宏任務(wù)吧,后面再遇到的話,繼續(xù)創(chuàng)建;
報(bào)告!代碼執(zhí)行到底了,當(dāng)前這個(gè)宏任務(wù)執(zhí)行完畢!
行,看一下咱的小尾巴---咱的微任務(wù)里面有代碼嗎?有的話直接執(zhí)行;報(bào)告,微任務(wù)里面,那兩個(gè)Promise的異步代碼執(zhí)行完了!
干的漂亮。。。對(duì)了,剛剛微任務(wù)里面,有沒(méi)有新的Promise微任務(wù)?有的話,繼續(xù)在現(xiàn)在這個(gè)微任務(wù)后面放!對(duì)對(duì),只看執(zhí)行到的代碼,有多少放多少,一會(huì)兒直接就執(zhí)行了。。。如果遇到了setTimeout知道該怎么做吧?繼續(xù)開(kāi)宏任務(wù)!報(bào)告,微任務(wù)全部執(zhí)行完畢!
好!開(kāi)始執(zhí)行下一個(gè)宏任務(wù)!
所以,現(xiàn)在如果執(zhí)行下面的代碼,結(jié)果也顯而易見(jiàn)吧:
console.log("同步-0.1") Promise.resolve().then(() => { console.log("P-1.1") Promise.resolve().then(() => { // 新加行 console.log("P-2.1") // 新加行 Promise.resolve().then(() => { // 新加行 console.log("P-3.1") // 新加行 }) // 新加行 }) // 新加行 }) setTimeout(() => { console.log("S-1.1") }); Promise.resolve().then(() => { console.log("P-1.2") }) setTimeout(() => { console.log("S-1.2") }); console.log("同步-0.2")
執(zhí)行結(jié)果如下:
同步-0.1 同步-0.2 P-1.1 P-1.2 P-2.1 P-3.1 S-1.1 S-1.2
無(wú)論Promise套用多少層,都會(huì)在下一個(gè)setTimeout之前執(zhí)行。
Dom操作到底是同步,還是異步?這里出現(xiàn)一個(gè)說(shuō)不清道不明的疑問(wèn),Dom操作到底是同步操作還是異步操作?
如果是同步操作,那vue的nextTick方法是做什么用的?不就是在Dom更新完之后的回調(diào)方法嗎?
如果是異步操作,那在劇烈操作Dom后面的代碼,為什么會(huì)被阻塞?而且代碼看上去,也的確是按順序執(zhí)行的?
這里直接說(shuō)明:js里面的Dom操作代碼,是同步執(zhí)行,但瀏覽器進(jìn)行的Dom渲染,是異步操作。
瀏覽器渲染Dom和執(zhí)行js,同時(shí)只能二選一,渲染一次Dom的時(shí)機(jī)是,當(dāng)前宏任務(wù)和小尾巴微任務(wù)執(zhí)行完,下一個(gè)宏任務(wù)開(kāi)始前
vue的nextTick方法,則是使用H5的Api---MutationObserver,監(jiān)聽(tīng)瀏覽器將Dom渲染完成的時(shí)機(jī)。若瀏覽器不支持此方法,則會(huì)使用setTimeout,把nextTick回調(diào)函數(shù)的執(zhí)行時(shí)機(jī),作為一個(gè)宏任務(wù);
上面也說(shuō)了,瀏覽器渲染一次Dom,是下一個(gè)宏任務(wù)開(kāi)始前,這樣使用了setTimeout,保證了Dom確實(shí)渲染完成。
這里也需要稍作提醒,js操作Dom是同步的,但操作Dom,畢竟超出了js本身語(yǔ)言的Api,每操作一次Dom,都需要消耗一定的性能,所以,在適合的情況下,最好先把要修改的Dom的內(nèi)容,以字符串或者虛擬Dom的形式拼接好,然后操作一次Dom,把組裝好的Dom字符串或虛擬Dom,一次性的塞進(jìn)HTML頁(yè)面的真實(shí)Dom中。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/108882.html
摘要:事件觸發(fā)線程主要負(fù)責(zé)將準(zhǔn)備好的事件交給引擎線程執(zhí)行。進(jìn)程瀏覽器渲染進(jìn)程瀏覽器內(nèi)核,主要負(fù)責(zé)頁(yè)面的渲染執(zhí)行以及事件的循環(huán)。第二輪循環(huán)結(jié)束。 將自己讀到的比較好的文章分享出來(lái),大家互相學(xué)習(xí),各位大佬有好的文章也可以留個(gè)鏈接互相學(xué)習(xí),萬(wàn)分感謝! 線程與進(jìn)程 關(guān)于線程與進(jìn)程的關(guān)系可以用下面的圖進(jìn)行說(shuō)明: showImg(https://segmentfault.com/img/bVbjSZt?...
摘要:事件觸發(fā)線程主要負(fù)責(zé)將準(zhǔn)備好的事件交給引擎線程執(zhí)行。進(jìn)程瀏覽器渲染進(jìn)程瀏覽器內(nèi)核,主要負(fù)責(zé)頁(yè)面的渲染執(zhí)行以及事件的循環(huán)。第二輪循環(huán)結(jié)束。 將自己讀到的比較好的文章分享出來(lái),大家互相學(xué)習(xí),各位大佬有好的文章也可以留個(gè)鏈接互相學(xué)習(xí),萬(wàn)分感謝! 線程與進(jìn)程 關(guān)于線程與進(jìn)程的關(guān)系可以用下面的圖進(jìn)行說(shuō)明: showImg(https://segmentfault.com/img/bVbjSZt?...
js運(yùn)行機(jī)制-事件循環(huán)EventLoop 先來(lái)看看一段js代碼: console.log(script begin) setTimeout(() => { console.log(setTimeout) },0) new Promise((resolve) => { console.log(promise begin) for(let i = 0; i < 1000; i...
摘要:是怎么執(zhí)行的一開(kāi)始先簡(jiǎn)單聊了聊基本的數(shù)據(jù)結(jié)構(gòu),它和我們現(xiàn)在說(shuō)的事件環(huán)有什么關(guān)系么當(dāng)然有,首先要明確的一點(diǎn)是,代碼的執(zhí)行全都在棧里,不論是同步代碼還是異步代碼,這個(gè)一定要清楚。 棧和隊(duì)列 在計(jì)算機(jī)內(nèi)存中存取數(shù)據(jù),基本的數(shù)據(jù)結(jié)構(gòu)分為棧和隊(duì)列。 棧(Stack)是一種后進(jìn)先出的數(shù)據(jù)結(jié)構(gòu),注意,有時(shí)候也管棧叫做堆棧,但是堆又是另一種復(fù)雜的數(shù)據(jù)結(jié)構(gòu),它和棧完全是兩碼事。棧的特點(diǎn)是操作只在一端進(jìn)行...
閱讀 2784·2021-10-11 11:08
閱讀 1503·2021-09-30 09:48
閱讀 1062·2021-09-22 15:29
閱讀 1049·2019-08-30 15:54
閱讀 990·2019-08-29 15:19
閱讀 541·2019-08-29 13:12
閱讀 3176·2019-08-26 13:53
閱讀 979·2019-08-26 13:28