<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          7關(guān)!setTimeout+Promise+Async輸出順序?你能過幾關(guān)!

          共 9884字,需瀏覽 20分鐘

           ·

          2021-10-15 00:56

          前言

          大家好,我是林三心,有關(guān)于EventLoop的知識點(diǎn),在平時(shí)是考的非常多的,其實(shí)也跟我們?nèi)粘5墓ぷ鲿r(shí)息息相關(guān)的,懂得EventLoop的執(zhí)行順序,可以大大幫助我們定位出問題出在哪。其實(shí)正常的EventLoop順序是很容易分辨的,但是如果setTimeout + Promise + async/await聯(lián)起手來是非常棘手的。今天我就帶大家過五關(guān)斬六將,征服他們?。。?/p>

          注明:本文不涉及Nodejs執(zhí)行機(jī)制

          同步 && 異步

          什么是異步,什么是同步,我不多說,我就通過小故事來講講吧。

          • 同步:你打電話去書店訂書,老板說我查查,你不掛電話在等待,老板把查到的結(jié)果告訴你,這期間你不能做自己的事情
          • 異步:你打電話去書店訂書,老板說我查查,回頭告訴你,你把電話掛了,先去做自己的事情

          JS執(zhí)行機(jī)制

          其實(shí)不難,JavaScript代碼執(zhí)行機(jī)制,我就歸結(jié)為三句話

          • 1、遇到同步代碼直接執(zhí)行
          • 2、遇到異步代碼先放一邊,并且將他回調(diào)函數(shù)存起來,存的地方叫事件隊(duì)列
          • 3、等所有同步代碼都執(zhí)行完,再從事件隊(duì)列中把存起來的所有異步回調(diào)函數(shù)拿出來按順序執(zhí)行
          截屏2021-10-04 下午8.11.18.png

          請看以下例子

          console.log(1)?//?同步
          setTimeout(()?=>?{
          ??console.log(2)?//?異步
          },?2000);
          console.log(3)?//?同步
          setTimeout(()?=>?{
          ??console.log(4)?//?異步
          },?0);
          console.log(5)?//?同步

          輸出?:?1?3?5?4?2
          截屏2021-10-04 下午9.11.39.png

          宏任務(wù) && 微任務(wù)

          前面說了,等所有同步代碼都執(zhí)行完,再從事件隊(duì)列里依次執(zhí)行所有異步回調(diào)函數(shù)

          其實(shí)事件隊(duì)列也是一個(gè)小團(tuán)體,人家也有自己的規(guī)則,這就類似于學(xué)校管理著許多社團(tuán),人家自己社團(tuán)內(nèi)部也有人家自己的規(guī)矩。

          話說回來,為什么事件隊(duì)列里需要有自己的規(guī)則呢?要不你先想想為什么學(xué)校里的社團(tuán)里要有自己的規(guī)則要分等級,是因?yàn)橛械娜四芰?qiáng)有的人能力弱,所以也就有了等級的高低。其實(shí)事件隊(duì)列也一樣,事件隊(duì)列是用來存異步回調(diào)的,但是異步也分類型啊,異步任務(wù)分為宏任務(wù)微任務(wù),并且微任務(wù)執(zhí)行時(shí)機(jī)先于宏任務(wù)

          那宏任務(wù)和微任務(wù)都分別有哪些呢?

          宏任務(wù)

          #瀏覽器Node
          I/O??
          setTimeout??
          setInterval??
          setImmediate??
          requestAnimationFrame??

          微任務(wù)

          #瀏覽器Node
          Promise.prototype.then catch finally??
          process.nextTick??
          MutationObserver??

          執(zhí)行流程

          那就來說說整體的執(zhí)行的流程吧

          截屏2021-10-05 下午4.37.02.png

          例子

          大家可以根據(jù)我的解題步驟去走,基本90%的題目都是沒什么壓力的?。?!

          • 1、標(biāo)記區(qū)分異步和同步
          • 2、異步中,標(biāo)記區(qū)分宏任務(wù)和微任務(wù)
          • 3、分輪數(shù),一輪一輪慢慢走
          console.log(1)?//?同步
          setTimeout(()?=>?{
          ??console.log(2)?//?異步:宏任務(wù)
          });
          console.log(3)?//?同步
          Promise.resolve().then(()=>{?//?異步:微任務(wù)
          ??console.log(4)?
          })
          console.log(5)?//?同步

          第一輪

          • 說明:先把同步的執(zhí)行輸出
          • 輸出:1,3,5
          • 產(chǎn)生宏任務(wù):setTimeout,產(chǎn)生微任務(wù):Promise.prototype.then

          第二輪

          • 說明:微任務(wù)先執(zhí)行
          • 輸出:4
          • 產(chǎn)生宏任務(wù):無,產(chǎn)生微任務(wù):無
          • 剩余宏任務(wù):setTimeout,剩余微任務(wù):無

          第三輪(結(jié)束)

          • 說明:執(zhí)行宏任務(wù)
          • 輸出:2
          • 產(chǎn)生宏任務(wù):無,產(chǎn)生微任務(wù):無
          • 剩余宏任務(wù):無,剩余微任務(wù):無

          第一關(guān)

          想一想我剛剛說的解題思路,大家可以按照那個(gè)思路來,這道題也就是分分鐘的事情啦

          console.log(1)
          setTimeout(()?=>?{
          ??console.log(2)
          ??Promise.resolve().then(()?=>?{
          ????console.log(3)
          ??})
          });
          console.log(4)
          new?Promise((resolve,reject)?=>?{
          ??console.log(5)
          }).then(()?=>?{
          ??console.log(6)
          ??setTimeout(()?=>?{
          ????console.log(7)
          ??})
          })
          console.log(8)

          第一步:標(biāo)記

          注意:Promise的executor是同步的哦?。。?/p>

          console.log(1)?//?同步
          setTimeout(()?=>?{
          ??console.log(2)?//?異步:宏任務(wù) setTimeout1
          ??Promise.resolve().then(()?=>?{?//?異步:微任務(wù) then1
          ????console.log(3)
          ??})
          });
          console.log(4)?//?同步
          new?Promise((resolve,reject)?=>?{
          ??console.log(5)?//?同步
          ??resolve()
          }).then(()?=>?{?//?異步:微任務(wù) then2
          ??console.log(6)
          ??setTimeout(()?=>?{
          ????console.log(7)?//?異步:宏任務(wù) setTimeout2
          ??})
          })
          console.log(8)?//?同步

          第二步:分輪

          輪數(shù)說明輸出產(chǎn)生剩余
          第一輪執(zhí)行外層同步輸出1,4,5,8宏任務(wù):setTimeout1
          微任務(wù):then2
          宏任務(wù):setTimeout1
          微任務(wù):then2
          第二輪執(zhí)行微任務(wù)then26宏任務(wù):setTimeout2
          微任務(wù):無
          宏任務(wù):setTimeout1,setTimeout2
          微任務(wù):無
          第三輪執(zhí)行宏任務(wù)setTimeout12宏任務(wù):無
          微任務(wù):then1
          宏任務(wù):setTimeout2
          微任務(wù):then1
          第四輪執(zhí)行微任務(wù)then13宏任務(wù):無
          微任務(wù):無
          宏任務(wù):setTimeout2
          微任務(wù):無
          第五輪執(zhí)行宏任務(wù)setTimeout27宏任務(wù):無
          微任務(wù):無
          宏任務(wù):無
          微任務(wù):無

          第二關(guān)

          大家在遇到Promise.then.then這種時(shí),如果有點(diǎn)懵逼的同學(xué),可以轉(zhuǎn)換一下,下面會說到

          注意:then方法會自動返回一個(gè)新的Promise,也就是return new Promise,具體的Promise源碼,大家可以看我這篇看了就會,手寫Promise原理,最通俗易懂的版本【閱讀:1.1w,點(diǎn)贊:430】

          setTimeout(()?=>?{
          ??console.log(1)
          },?0)
          console.log(2)
          const?p?=?new?Promise((resolve)?=>?{
          ??console.log(3)
          ??resolve()
          }).then(()?=>?{
          ??console.log(4)
          }).then(()?=>?{
          ??console.log(5)
          })
          console.log(6)

          第一步:標(biāo)記 + 轉(zhuǎn)換

          注意:這里的轉(zhuǎn)換,只針對做題時(shí),比較好理解,平時(shí)不要這么轉(zhuǎn)換,平時(shí)這么轉(zhuǎn)換是不太合適的,是錯的

          setTimeout(()?=>?{?//?異步:宏任務(wù) setTimeout
          ??console.log(1)
          },?0)
          console.log(2)?//?同步
          const?p?=?new?Promise((resolve)?=>?{?//?p?是?then1?執(zhí)行返回的新?Promise
          ??console.log(3)?//?同步
          ??resolve()
          }).then(()?=>?{?//?異步:微任務(wù) then1
          ??console.log(4)
          ??//?拿著?p?重新?then
          ??p.then(()?=>?{?//?異步:微任務(wù) then2
          ????console.log(5)
          ??})
          })
          console.log(6)?//?同步?6

          第二步:分輪

          輪數(shù)說明輸出產(chǎn)生剩余
          第一輪執(zhí)行同步輸出2,3,6宏任務(wù):setTimeout
          微任務(wù):then1
          宏任務(wù):setTimeout
          微任務(wù):then1
          第二輪執(zhí)行微任務(wù)then14宏任務(wù):無
          微任務(wù):then2
          宏任務(wù):setTimeout
          微任務(wù):then2
          第三輪執(zhí)行微任務(wù)then25宏任務(wù):無
          微任務(wù):無
          宏任務(wù):setTimeout
          微任務(wù):無
          第四輪執(zhí)行宏任務(wù)setTimeout1宏任務(wù):無
          微任務(wù):無
          宏任務(wù):無
          微任務(wù):無

          第三關(guān)

          再說一遍:大家在遇到Promise.then.then這種時(shí),如果有點(diǎn)懵逼的同學(xué),可以轉(zhuǎn)換一下

          注意:then方法會自動返回一個(gè)新的Promise,也就是return new Promise,具體的Promise源碼,大家可以看我這篇看了就會,手寫Promise原理,最通俗易懂的版本【閱讀:1.1w,點(diǎn)贊:430】

          new?Promise((resolve,reject)=>{
          ??console.log(1)
          ??resolve()
          }).then(()=>{
          ??console.log(2)
          ??new?Promise((resolve,reject)=>{
          ??????console.log(3)
          ??????resolve()
          ??}).then(()=>{
          ??????console.log(4)
          ??}).then(()=>{
          ??????console.log(5)
          ??})
          }).then(()=>{
          ??console.log(6)
          })

          第一步:標(biāo)記 + 轉(zhuǎn)換

          注意:這里的轉(zhuǎn)換,只針對做題時(shí),比較好理解,平時(shí)不要這么轉(zhuǎn)換,平時(shí)這么轉(zhuǎn)換是不太合適的,是錯的

          const?p1?=?new?Promise((resolve,?reject)?=>?{?//?p1?是?then1?執(zhí)行返回的新?Promise
          ??console.log(1)?//?同步
          ??resolve()
          }).then(()?=>?{?//?異步:微任務(wù) then1
          ??console.log(2)
          ??const?p2?=?new?Promise((resolve,?reject)?=>?{?//?p2?是?then2?執(zhí)行返回的新?Promise
          ????console.log(3)?//?then1?里的?同步
          ????resolve()
          ??}).then(()?=>?{?//?異步:微任務(wù) then2
          ????console.log(4)
          ????
          ????//?拿著?p2?重新?then
          ????p2.then(()?=>?{?//?異步:微任務(wù) then3
          ??????console.log(5)
          ????})
          ??})
          ??
          ??//?拿著?p1?重新?then
          ??p1.then(()?=>?{?//?異步:微任務(wù) then4
          ????console.log(6)
          ??})
          })

          第二步:分輪

          輪數(shù)說明輸出產(chǎn)生剩余
          第一輪執(zhí)行外層同步輸出1宏任務(wù):無
          微任務(wù):then1
          宏任務(wù):無
          微任務(wù):then1
          第二輪執(zhí)行微任務(wù)then12,3宏任務(wù):無
          微任務(wù):then2、then4
          宏任務(wù):無
          微任務(wù):then2、then4
          第三輪執(zhí)行微任務(wù)then2,then44,6宏任務(wù):無
          微任務(wù):then3
          宏任務(wù):無
          微任務(wù):then3
          第四輪執(zhí)行微任務(wù)then35宏任務(wù):無
          微任務(wù):無
          宏任務(wù):無
          微任務(wù):無

          第四關(guān)

          這一關(guān),比上一關(guān)多了一個(gè)return

          前面說了,then方法會自動返回一個(gè)新的Promise,相當(dāng)于return new Promise,但是如果你手動寫了return Promise,那return的就是你手動寫的這個(gè)Promise

          new?Promise((resolve,?reject)?=>?{
          ??console.log(1)
          ??resolve()
          }).then(()?=>?{
          ??console.log(2)
          ??//?多了個(gè)return
          ??return?new?Promise((resolve,?reject)?=>?{
          ????console.log(3)
          ????resolve()
          ??}).then(()?=>?{
          ????console.log(4)
          ??}).then(()?=>?{?//?相當(dāng)于return了這個(gè)then的執(zhí)行返回Promise
          ????console.log(5)
          ??})
          }).then(()?=>?{
          ??console.log(6)
          })

          第一步:標(biāo)記 + 轉(zhuǎn)換

          由于return的是then3執(zhí)行返回的Promise,所以then4其實(shí)是then3Promise.then(),所以可轉(zhuǎn)換為then3.then4

          new?Promise((resolve,?reject)?=>?{
          ??console.log(1)?//?同步
          ??resolve()
          }).then(()?=>?{?//?異步:微任務(wù) then1
          ??console.log(2)?//?then1?中的?同步
          ??new?Promise((resolve,?reject)?=>?{
          ????console.log(3)?//?then1?中的?同步
          ????resolve()
          ??}).then(()?=>?{?//?異步:微任務(wù) then2
          ????console.log(4)
          ??}).then(()?=>?{?//?異步:微任務(wù) then3
          ????console.log(5)
          ??}).then(()?=>?{?//?異步:微任務(wù) then4
          ????console.log(6)
          ??})
          })

          第二步:分輪

          輪數(shù)說明輸出產(chǎn)生剩余
          第一輪執(zhí)行外層同步輸出1宏任務(wù):無
          微任務(wù):then1
          宏任務(wù):無
          微任務(wù):then1
          第二輪執(zhí)行微任務(wù)then12,3宏任務(wù):無
          微任務(wù):then2、then3、then4
          宏任務(wù):無
          微任務(wù):then2、then3、then4
          第三輪執(zhí)行微任務(wù)then2、then3、then44,5,6宏任務(wù):無
          微任務(wù):無
          宏任務(wù):無
          微任務(wù):無

          第五關(guān)

          new?Promise((resolve,?reject)?=>?{
          ??console.log(1)
          ??resolve()
          }).then(()?=>?{
          ??console.log(2)
          ??new?Promise((resolve,?reject)?=>?{
          ????console.log(3)
          ????resolve()
          ??}).then(()?=>?{
          ????console.log(4)
          ??}).then(()?=>?{
          ????console.log(5)
          ??})
          }).then(()?=>?{
          ??console.log(6)
          })
          new?Promise((resolve,?reject)?=>?{
          ??console.log(7)
          ??resolve()
          }).then(()?=>?{
          ??console.log(8)
          })

          第一步:標(biāo)記 + 轉(zhuǎn)換

          const?p1?=?new?Promise((resolve,?reject)?=>?{?//?p1?是?then1?執(zhí)行返回的新?Promise
          ??console.log(1)?//?同步
          ??resolve()
          }).then(()?=>?{?//?異步:微任務(wù) then1
          ??console.log(2)
          ??const?p2?=?new?Promise((resolve,?reject)?=>?{?//?p2?是?then2?執(zhí)行返回的新?Promise
          ????console.log(3)?//?then1?里的?同步
          ????resolve()
          ??}).then(()?=>?{?//?異步:微任務(wù) then2
          ????console.log(4)
          ????
          ????//?拿著?p2?重新?then
          ????p2.then(()?=>?{?//?異步:微任務(wù) then3
          ??????console.log(5)
          ????})
          ??})
          ??
          ??//?拿著?p1?重新?then
          ??p1.then(()?=>?{?//?異步:微任務(wù) then4
          ????console.log(6)
          ??})
          })

          new?Promise((resolve,?reject)?=>?{
          ??console.log(7)?//?同步
          ??resolve()
          }).then(()?=>?{??//?異步:微任務(wù) then5
          ??console.log(8)
          })

          第二步:分輪

          輪數(shù)說明輸出產(chǎn)生剩余
          第一輪執(zhí)行外層同步輸出1,7宏任務(wù):無
          微任務(wù):then1、then5
          宏任務(wù):無
          微任務(wù):then1、then5
          第二輪執(zhí)行微任務(wù)then1、then52,3,8宏任務(wù):無
          微任務(wù):then2、then4
          宏任務(wù):無
          微任務(wù):then2、then4
          第三輪執(zhí)行微任務(wù)then2、then44,6宏任務(wù):無
          微任務(wù):then5
          宏任務(wù):無
          微任務(wù):then5
          第四輪執(zhí)行微任務(wù)then55宏任務(wù):無
          微任務(wù):無
          宏任務(wù):無
          微任務(wù):無

          第六關(guān)

          其實(shí)async/await的內(nèi)部實(shí)現(xiàn)原理,是依賴于Promise.prototype.then的不斷嵌套,它在題中也是可以轉(zhuǎn)換的,下面會講到。

          有興趣的朋友可以看我這篇7張圖,20分鐘就能搞定的async/await原理!為什么要拖那么久【閱讀量:1.8w,點(diǎn)贊:571】

          async?function?async1()?{
          ??console.log(1);
          ??await?async2();
          ??console.log(2);
          }
          async?function?async2()?{
          ??console.log(3);
          }
          console.log(4);
          setTimeout(function?()?{
          ??console.log(5);
          });
          async1()
          new?Promise(function?(resolve,?reject)?{
          ??console.log(6);
          ??resolve();
          }).then(function?()?{
          ??console.log(7);
          });
          console.log(8);

          第一步:標(biāo)記 + 轉(zhuǎn)換

          注意:這里的轉(zhuǎn)換,只針對做題時(shí),比較好理解,平時(shí)不要這么轉(zhuǎn)換,平時(shí)這么轉(zhuǎn)換是不太合適的

          console.log(4);?//?同步
          setTimeout(function?()?{
          ??console.log(5);?//?異步:宏任務(wù) setTimeout
          });

          //?async1函數(shù)可轉(zhuǎn)換成
          console.log(1)?//?同步
          new?Promise((resolve,?reject)?=>?{
          ??console.log(3)?//?同步
          ??resolve()
          }).then(()?=>?{?//?異步:微任務(wù) then1
          ??console.log(2)
          })
          //?async1函數(shù)結(jié)束

          new?Promise(function?(resolve,?reject)?{
          ??console.log(6);?//?同步
          ??resolve();
          }).then(function?()?{?//?異步:微任務(wù) then2
          ??console.log(7);
          });
          console.log(8);?//?同步

          第二步:分輪

          輪數(shù)說明輸出產(chǎn)生剩余
          第一輪執(zhí)行同步輸出4,1,3,6,8宏任務(wù):setTimeout
          微任務(wù):then1、then2
          宏任務(wù):setTimeout
          微任務(wù):then1、then2
          第二輪執(zhí)行微任務(wù)then1、then22,7宏任務(wù):無
          微任務(wù):無
          宏任務(wù):setTimeout
          微任務(wù):無
          第三輪執(zhí)行宏任務(wù)setTimeout5宏任務(wù):無
          微任務(wù):then5
          宏任務(wù):無
          微任務(wù):無

          課后作業(yè)

          最后給大家布置兩道作業(yè),幫大家鞏固一下本文章所學(xué)的知識,大家也可以加入我的摸魚群,進(jìn)行答案的討論。進(jìn)群點(diǎn)擊這里進(jìn)群,目前已有將近1000人加入學(xué)習(xí),我會定時(shí)舉辦學(xué)習(xí)分享,模擬面試等學(xué)習(xí)活動,一起學(xué)習(xí),共同進(jìn)步?。?!

          第一題(思考題)

          想一想下面這兩個(gè)有什么區(qū)別?

          //?第一種
          const?p?=?new?Promise((resolve,?reject)?=>?{
          ??resolve()
          }).then(()?=>?console.log(1)).then(()?=>?console.log(2))

          //?第二種
          const?p?=?new?Promise((resolve,?reject)?=>?{
          ??resolve()
          })
          p.then(()?=>?console.log(1))
          p.then(()?=>?console.log(2))

          第二題(問題不大)

          async?function?async1()?{
          ??console.log(1);
          ??await?async2();
          ??console.log(2);
          }
          async?function?async2()?{
          ??console.log(3);
          }

          new?Promise((resolve,?reject)?=>?{
          ??setTimeout(()?=>?{
          ????resolve()
          ????console.log(4)
          ??},?1000);
          }).then(()?=>?{
          ??console.log(5)
          ??new?Promise((resolve,?reject)?=>?{
          ????setTimeout(()?=>?{
          ??????async1()
          ??????resolve()
          ??????console.log(6)
          ????},?1000)
          ??}).then(()?=>?{
          ????console.log(7)
          ??}).then(()?=>?{
          ????console.log(8)
          ??})
          }).then(()?=>?{
          ??console.log(9)
          })
          new?Promise((resolve,?reject)?=>?{
          ??console.log(10)
          ??setTimeout(()?=>?{
          ????resolve()
          ????console.log(11)
          ??},?3000);
          }).then(()?=>?{
          ??console.log(12)
          })

          第三題(有點(diǎn)難度)

          這道題能一分鐘內(nèi)做出來的找我領(lǐng)獎,這道題需要具備一定的Promise原理基礎(chǔ) + async/await原理基礎(chǔ)才能比較輕松的答對,有興趣的同學(xué)可以看我之前寫過的文章

          • 看了就會,手寫Promise原理,最通俗易懂的版本【閱讀:1.1w,點(diǎn)贊:430】
          • 7張圖,20分鐘就能搞定的async/await原理!為什么要拖那么久【閱讀量:1.8w,點(diǎn)贊:571】
          async?function?async1()?{
          ??console.log('async1?start')
          ??await?async2()
          ??console.log('async1?end')
          }

          async?function?async2()?{
          ??console.log('async?start')
          ??return?new?Promise((resolve,?reject)?=>?{
          ????resolve()
          ????console.log('async2?promise')
          ??})
          }

          console.log('script?start')
          setTimeout(()?=>?{
          ??console.log('setTimeout')
          },?0);

          async1()

          new?Promise((resolve)?=>?{
          ??console.log('promise1')
          ??resolve()
          }).then(()?=>?{
          ??console.log('promise2')
          }).then(()?=>?{
          ??console.log('promise3')
          })
          console.log('script?end')

          結(jié)語

          加入我的摸魚群 ,我會定時(shí)直播模擬面試,答疑解惑,先已有將近1000人加入學(xué)習(xí)

          image.png


          瀏覽 48
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  天天爽夜夜爽人人爽 | 男生操逼网站 | 免费高清不卡a无码 | 在线毛片网址 | 国产内射一级毛片农民工 |