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