定時器和promise_從Promise鏈理解EventLoop

面試題

new Promise(resolve => {  setTimeout(()=>{    console.log(666);    new Promise(resolve => {     resolve();    })    .then(() => {console.log(777);})  })  resolve(); }) .then(() => {        new Promise(resolve => {          resolve();        })       .then(() => {console.log(111);})       .then(() => {console.log(222);}); }) .then(() => {       new Promise((resolve) => {         resolve()       })       .then(() => {             new Promise((resolve) => {               resolve()             })            .then(() => {console.log(444)})      })      .then(() => {        console.log(555);      })}).then(() => {  console.log(333);})  

答案

111222333444555666777

如果你沒有得出正確的結果,有必要繼續往下看.

為了能正確解答上題,需要對宏任務、微任務以及Event-Loop深入理解.

知識點

宏任務

瀏覽器執行代碼的過程中,JS引擎會將大部分代碼進行分類,分別分到這兩個隊列中--宏任務(macrotask ) 和 微任務(microtask ) .

常見的宏任務:script(整體代碼), XHR回調,setTimeout, setInterval, setImmediate(node獨有), I/O.

上面的描述仍然有些生澀,下面借助案例深入理解.

app.js

    setTimeout(()=>{ //宏任務2      console.log(2);    },0)    setTimeout(()=>{  //宏任務3      console.log(3);     },0)    console.log(1);

執行結果: 1 -- 2 -- 3

?瀏覽器開始運行 app.js 時啟動了第一個宏任務(宏任務1,指向app.js整體代碼)并開始執行.?在執行宏任務1途中遇到了第一個定時器,瀏覽器便會開啟一個新的宏任務2,定時器被添加到宏任務隊列等待,線程繼續往下執行.?隨后又遇到了定時器開啟一個新的宏任務3,定時器又被添加到宏任務隊列等待,宏任務3排在宏任務2的后面,線程繼續往下執行.?線程走到最后輸出了1,此時宏任務1就結束了.瀏覽器此刻就會去宏任務隊列中尋找,排在最前面的是宏任務2,發現延遲時間已到允許執行便輸出了2,宏任務2結束又執行宏任務3輸出3.

宏任務通常是由宿主環境開啟.比如在客戶端,瀏覽器就是宿主環境.開始執行一個腳本文件,開啟一個定時器任務以及ajax請求,都是瀏覽器在其底層完成,并非是通過js 引擎去做的這些工作.在服務器端,node就作為了宿主環境.

微任務

微任務是宏任務的組成部分,微任務與宏任務是包含關系,并非前后并列.如果要談微任務,需要指出它屬于哪個宏任務才有意義.

常見的宏任務:process.nextTick(nodejs端),Promise等.

app.js

    console.log(1);    new Promise((resolve)=>{        resolve();    }).then(()=>{        console.log(2)    })    console.log(3)

執行結果: 1 -- 3 -- 2

?運行 app.js 腳本文件啟動宏任務1,第一行代碼執行輸出1.?碰到Promise,將then的回調函數放入宏任務1的微任務隊列中等待,線程繼續往下.?代碼跑到最后一行輸出3.此時同步代碼執行完畢,開始檢查當前宏任務中的微任務隊列.?運行微任務隊列中的第一個then回調函數輸出2.再檢查微任務隊列,沒有發現其他任務.?微任務隊列執行完畢,宏任務1執行完畢.

宏任務由宿主環境開啟,與此相對應,微任務是 js 引擎從代碼層面開啟的.

如果還對宏任務和微任務的關系模棱兩可,下面從 Event-Loop 角度詳細闡述.

Event-Loop

5a25b02260669dc10a7c0b352b68ff73.png
Event-Loop

從上圖可知,宏任務形成了一個擁有先后順序的隊列.每個宏任務中分為同步代碼和微任務隊列.

?假設js當前的線程執行宏任務1,先執行宏任務1中的同步代碼.?如果碰到Promise或者process.nextTick,就把它們的回調放入當前宏任務1的微任務隊列中.?如果碰到setTimeout, setInterval之類就會在當前宏任務1的隊列后面開啟新的宏任務將回調放入其中.?同步代碼執行完,開始執行宏任務1的微任務隊列,直到微任務隊列的所有任務都執行完.?微任務隊列的所有任務執行完畢,宏任務1再看沒有其他代碼了,當前的事件循環結束.js線程開始執行下一個宏任務,直到所有宏任務執行完畢.如此整體便構成了事件循環機制.

延伸

dom操作屬于宏任務還是微任務

 console.log(1); document.getElementById("div").style.color = "red"; console.log(2);

在實踐中發現,當上面代碼執行到第三行時,控制臺輸出了1并且頁面已經完成了重繪,div的顏色變成了紅色.

dom操作它既不是宏任務也不是微任務,它應該歸于同步執行的范疇.

requestAnimationFrame屬于宏任務還是微任務

setTimeout(() => {  console.log("11111")}, 0)requestAnimationFrame(() => {   console.log("22222")})new Promise(resolve => {  console.log('promise');  resolve();}).then(() => {console.log('then')})

執行結果: promise -- then -- 22222 -- 11111

很多人會把 requestAnimationFrame 歸結到宏任務中,因為發現它會在微任務隊列完成后執行.

但實際上 requestAnimationFrame 它既不能算宏任務,也并非是微任務.它的執行時機是在當前宏任務范圍內,執行完同步代碼和微任務隊列后再執行.它仍然屬于宏任務范圍內,但是是在微任務隊列執行完畢后才執行.

Promise的運行機制

包裹函數是同步代碼

 new Promise((resolve)=>{    console.log(1);    resolve();  }).then(()=>{    console.log(2); })

new Promise里面的包裹的函數,也就是輸出1的那段代碼是同步執行的.而then包裹的函數才會被加載到微任務隊列中等待執行.

Promise鏈條如果沒有return

new Promise((resolve)=>{    console.log(1)    resolve();}).then(()=>{    console.log(2);}).then(()=>{    console.log(3);}).then(()=>{    console.log(4);})

執行結果: 1 -- 2 -- 3 -- 4

在平時開發中,在Promise鏈中通常會返回一個新的Promise做異步操作返回相應的值.如下.

new Promise((resolve)=>{    console.log(1)    resolve();}).then(()=>{     return new Promise((resolve)=>{       resolve(2)     })}).then((n)=>{    console.log(n);})

執行結果: 1 -- 2

但上述代碼中,then函數的回調里沒有返回任何東西.但是后續then包含的回調函數仍然會依次執行,返回 1 -- 2 -- 3 -- 4.并且它可以在末尾無限接then函數,這些函數也都會依次執行.

多個then函數執行次序

new Promise((resolve)=>{   // 1    console.log("a")         // 2             resolve();                // 3}).then(()=>{               // 4    console.log("b");       // 5}).then(()=>{               // 6    console.log("c");       // 7})                          // 8console.log("d")          // 9

執行結果: a -- d -- b -- c

?1,2,3行為同步執行的代碼,一氣呵成輸出 a.?此時線程走到第4行碰到then函數的回調,將其放入微任務的隊列等待.?線程繼續往后走直接跳到了第9行輸出了 d,為什么會忽略第6行的then直接跳到第9行呢?因為第4行的then函數回調執行完畢后才會開始執行第6行的代碼.(如果不理解為什么此刻會忽略掉第6行代碼可以查閱一下函數柯里化的概念).?同步代碼執行完畢,開始執行微任務隊列.此時微任務隊列里面只包含了一個then的回調函數,執行輸出b.?4,5行執行完畢后,開始執行第6行代碼.發現了then函數回調,將其放入微任務隊列中.此時第一個微任務執行完了,將其清空.?微任務隊列中還有一個剛放進去的微任務,執行輸出 c.清除此微任務,至此微任務隊列為空,全部任務執行完畢.

解題

有了以上知識的儲備再回到本文最初的面試題,這道題就可以輕松解決了.(為了方便闡述,加入右邊行號)

new Promise(resolve => {            // 1  setTimeout(()=>{                       // 2      console.log(666);                   // 3      new Promise(resolve => {     // 4        resolve();                                    })                                                .then(() => {console.log(777);})   // 7  })                                                 resolve();                                 // 9 })                                           // 10 .then(() => {                                // 11         new Promise(resolve => {        // 12           resolve();                              // 13         })         .then(() => {console.log(111);})    // 15         .then(() => {console.log(222);});   // 16 })                                                 // 17 .then(() => {                               // 18         new Promise((resolve) => {       // 19           resolve()         })        .then(() => {                               // 22             new Promise((resolve) => {        // 23               resolve()             })            .then(() => {console.log(444)})       // 26         })        .then(() => {                                   // 28           console.log(555);                   // 29        })}).then(() => {                       // 32  console.log(333);})  

?線程執行第一行代碼,同步執行Promise包裹的函數.?在第二行發現定時器,啟動一個宏任務,將定時器的回調放入宏任務隊列等待,線程直接跳到第9行執行?第9行執行完開始執行第11行代碼發現then函數,放入當前微任務隊列中.線程往后再沒有可以執行的代碼了,于是開始執行微任務隊列.?執行微任務隊列進入第12行代碼,運行到第15行代碼時發現then函數放入微任務隊列等待.隨后線程直接跳到第18行,碰到then函數放到微隊列中.后續沒有可執行的代碼了,再開始執行微任務隊列的第一個任務也就是第15行代碼輸出111.?15行執行完執行到16行碰到then回調放入微任務隊列等待.隨后線程跳到18行的微任務開始執行,一直執行到22行碰到then函數又放入微任務隊列等待.此時線程繼續往下跳到第32行碰到then函數放入微任務隊列等待.后續沒有可執行的代碼了,再開始執行微任務隊列的第一個任務.?線程跳到第16行執行微任務輸出 222,隨后又跳到22行執行下一個微任務,在26行處碰到then函數放入微任務隊列等待.線程繼續執行下一個微任務跳到32行輸出 333.至此這一輪的三個微任務全部執行完畢清空,又開始執行微任務隊列的第一個任務,線程跳到第26行輸出 444.?線程執行到28行碰到then函數回調放入微任務隊列等待.后續沒有可執行的代碼了,再開始執行微任務隊列的第一個任務即29行代碼輸出 555.?所有微任務執行完畢,當前宏任務結束.線程開始執行下一個宏任務,線程跳到第三行輸出 666.?線程繼續往后第7行碰到then回調放入微任務隊列,后續沒有可執行的代碼了,再開始執行微任務隊列的第一個任務輸出 777.第二個宏任務執行完畢.

綜上所述:輸出分別為 111 -- 222 -- 333 -- 444 -- 555 -- 666 -- 777

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/533753.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/533753.shtml
英文地址,請注明出處:http://en.pswp.cn/news/533753.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

ugui源碼_UGUI整體解決方案基礎篇(Unity 2019)

課程介紹:本課程是UGUI系列課程的第一篇:基礎篇主要是講解UGUI的基礎組件及接口的使用方法,目前UGUI是unity最常用的UI系統,這部分基礎只是是每個同學都應該掌握的,這里我就是簡單的講解了用法,大家對UGUI熟…

android 實例源碼解釋,Android Handler 原理分析及實例代碼

Android Handler 原理分析Handler一個讓無數android開發者頭疼的東西,希望我今天這邊文章能為您徹底根治這個問題今天就為大家詳細剖析下Handler的原理Handler使用的原因1.多線程更新Ui會導致UI界面錯亂2.如果加鎖會導致性能下降3.只在主線程去更新UI,輪詢處理Handl…

amd cpu排行_最新intel和amd處理器性能排行cpu天梯圖2019

現在市面上cpu廠家有很多,比如常見的intel系列的、amd系列CPU,cpu對電腦起著至關重要的作用,所以我們需要知道cpu性能的好壞,為此小編這就給大家帶來最新intel和amd處理器性能排行對比天梯圖,大家可以了解一下吧。inte…

python中的對象列表_Python內建的對象列表

Python內建的對象列表剛寫Python肯定會遇到這樣的情況,想寫些什么,但又不知從何寫起...在我看來問題在于我們不知道有什么東東可以拿來玩,這里列出Python的內建對象,稍微歸類了一下,多看幾遍代碼自然筆上生花&#xff…

電腦會顯示android,怎么在電腦上顯示、操作安卓手機

想要在電腦上顯示、操作安卓手機,該怎么辦,那么怎么在電腦上顯示、操作安卓手機的呢?下面是學習啦小編收集整理的怎么在電腦上顯示、操作安卓手機,希望對大家有幫助~~在電腦上顯示、操作安卓手機的方法工具/原料windows操作系統安卓手機電腦…

git version是什么軟件_Deepin 15.11 安裝 ZoneMinder 視頻監控軟件

Zoneminder是一款開源的視頻監控軟件,可以很方便的連接ip攝像頭。因計劃將家中的監控攝像頭引入NAS,在一臺deepin系統的筆記本是先進行了測試。UBUNTU和debian系統都是很容易安裝這個軟件的。未來在NAS上用docker啟動一個專門的zoneminder,do…

看不出svp補幀_專業補幀軟件SVP4 實現PotPlayer視頻補幀教程

雖然能實現幀率翻倍,不過現在視頻絕大多數都是24幀或25幀,翻倍也才48幀,沒辦法實現補幀后達到60幀的效果。SVP4是一款專業版視頻補幀軟件,提供GPU加速,并允許使用中檔CPU和幾乎任何GPU硬件為60Hz的FullHD 1080p視頻重新…

android 通知歷史,Android P新特性:追蹤應用通知歷史

原標題:Android P新特性:追蹤應用通知歷史IT之家3月9日消息 不久前,谷歌已經正式推出了首個Android P開發者預覽版,包含了許多新特性。對此,IT之家也進行了一系列報道。該系統的新特性也正在不斷被發現。例如最新消息顯…

文件另存為時名稱會改變_易經:人處在困境時,不要焦慮,改變固定習慣,就會迎來轉機...

我讀《易經》,悟到一些規律:人的一生,起起落落,時而順利,時而受困,都是正常現象,沒有必要把困難和壓力看得太重。人處在困境時,不要焦慮,只要改變你的固定習慣&#xff0…

ubuntu系統寫路由指令_在Ubuntu中如何查看網絡路由表詳解,

在Ubuntu中如何查看網絡路由表詳解,什么是Linux中的路由和路由表?路由的過程意味著IP包在網絡上從一點傳輸到另一點。當你向某人發送電子郵件時,你實際上是在將一系列IP數據包或數據報從你的系統傳輸到另一個人的計算機上。從計算機發送的數據…

jspdf html轉換pdf,使用jspdf將HTML轉換為pdf時出錯

對于一個角度項目,我試圖將包含HTML代碼的字符串變量轉換為pdf文件。我安置了所有的家屬,比如:jspdf格式光柵化HTML我的代碼如下:b64DecodeUnicode(str) {return decodeURIComponent(atob(str).split().map(function(c) {return % (00 c.charCodeAt(0).toString(16)).slice(-…

澄海哪里學機器人編程_終于發現小孩有必要學機器人編程嗎

讓孩子學習編程的目的,就像其他教育方式一樣,只是希望能幫助孩子找到他的興趣點,打開孩子的獲取知識和能力的大門。一起來看看一篇小孩有必要學機器人編程嗎。小孩有必要學機器人編程嗎編程和英語類似,屬于基本技能,未…

鴻蒙系統替代iOS,華為橫空出世!鴻蒙系統,能否替代安卓IOS?

原標題:華為橫空出世!鴻蒙系統,能否替代安卓IOS?從長遠來看,華為主推鴻蒙系統是必然的選擇。畢竟安卓系統為谷歌的,而由于美國限制,讓華為格外被動。命運掌握在自己手里,才有足夠的話…

ubuntu安裝python3.8_將 Ubuntu 16 和 18 上的 python 升級到最新 python3.8 的方法教程

1. 概述 本文記錄在 Ubuntu 16.04 上將 python 升級為 3.8 版本,并配置為系統默認 python3 的過程。 在 Ubuntu 16.04 中,python3 的默認版本為 3.5: $ python3 -V Python 3.5.2 本文以在 Ubuntu 16.04 中安裝為例,方法同樣適用于…

java怎么表示正無窮大_java中怎樣表示一個無窮大? 無窮小?

Java中提供了三個特殊的浮點數值:正無窮大、負無窮大、非數,用于表示溢出和出錯。正無窮大:用一個正數除以0將得到一個正無窮大,通過Double或Float的POSITIVE_INFINITY表示。負無窮大:用一個負數除以0將得到一個負無窮…

ng bind html 無效,angularjs中ng-bind-html的用法總結

本篇主要講解angular中的$sanitize這個服務.此服務依賴于ngSanitize模塊.(這個模塊需要加載angular-sanitize.js插件)要學習這個服務,先要了解另一個指令: ng-bing-html.顧名思義,ng-bind-html和ng-bind的區別就是,ng-bind把值作為字符串,和元素的內容進行綁定,但是ng-bind-htm…

熱門搜索怎么實現_三個步驟教你學會,搜索引擎霸屏技術!

做好SEO就要了解搜索引擎霸屏技術,它是在百度中搜索關鍵字來檢索信息。整個畫面的推薦都是你的內容。那么客戶點擊你的可能性就會增加!那么搜索引擎霸屏技術這么好,那要如何做到呢?1.要想成為霸屏,第一步要選擇好的關鍵…

ethtool用法 linux_Linux命令之Ethtool用法詳解

Linux/Unix命令之Ethtool描述:Ethtool是用于查詢及設置網卡參數的命令。概要:ethtool ethX //查詢ethX網口基本設置ethtool –h //顯示ethtool的命令幫助(help)ethtool –i ethX //查詢ethX網口的相關信息ethtool –d ethX //查詢ethX…

html字體如何設置垂直居中顯示,css文字水平垂直居中怎么設置?

css文字水平垂直居中怎么設置?下面本篇文章就來給大家介紹使用CSS設置文字水平居中和垂直居中的方法。有一定的參考價值,有需要的朋友可以參考一下,希望對大家有所幫助。1、文字水平居中在CSS中想要讓文字水平居中,可以使用text-a…

python for循環例子_Python for循環生成列表的實例

Python for循環生成列表的實例 一般Python for語句前不加語句,但我在機器學習實戰中看到了這兩條語句: featList [example[i] for example in dataSet] classList [example[-1] for example in dataSet] 多方研究和詢問,得到如下解釋&#…