<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>

          【JS】1034- Event Loop :你知道它們的打印順序嗎?

          共 8910字,需瀏覽 18分鐘

           ·

          2021-08-01 10:59

          前言

          《瀏覽器知識(shí)點(diǎn)整理(十三)不同的回調(diào)執(zhí)行時(shí)機(jī):宏任務(wù)和微任務(wù)》[1] 這篇文章的后面有幾道打印面試題,覺得不夠過癮,于是又找了一些對(duì)應(yīng)的面試題來,還在不同的 Node.js 版本上面跑了跑,希望你也能過過癮。

          先來回顧一下瀏覽器 Event Loop 的過程:

          • 先執(zhí)行 當(dāng)前調(diào)用棧中的同步代碼(一個(gè)宏任務(wù));
          • 調(diào)用棧為空后去檢查是否有異步任務(wù)(微任務(wù))需要執(zhí)行
          • 如果有則 執(zhí)行完當(dāng)前異步代碼(當(dāng)前宏任務(wù)中微任務(wù)隊(duì)列里的所有微任務(wù)),
          • 再之后就是 從消息隊(duì)列中取出下一個(gè)宏任務(wù) 去執(zhí)行(重新維護(hù)一個(gè)調(diào)用棧),然后開始新一輪的 Event Lopp。

          然后不同版本的 Node.js 的一個(gè) Event Loop 區(qū)別:

          • 如果是 Node 10 及其之前版本:宏任務(wù)隊(duì)列當(dāng)中有幾個(gè)宏任務(wù),是要等到宏任務(wù)隊(duì)列當(dāng)中的所有宏任務(wù)全部執(zhí)行完畢才會(huì)去執(zhí)行微隊(duì)列當(dāng)中的微任務(wù)。
          • 如果是 Node 11 及之后版本:一旦執(zhí)行一個(gè)階段里對(duì)應(yīng)宏隊(duì)列當(dāng)中的一個(gè)宏任務(wù)(setTimeoutsetIntervalsetImmediate 三者其中之一,不包括 I/O)就立刻執(zhí)行微任務(wù)隊(duì)列,執(zhí)行完微隊(duì)列當(dāng)中的所有微任務(wù)再回到剛才的宏隊(duì)列執(zhí)行下一個(gè)宏任務(wù)。這就 跟瀏覽器端運(yùn)行一致 了。

          為了過這個(gè)癮,我意用 nvmnvm install 10.13.0)安裝了 10.13.0 版本的 Node.js

          image.png

          參賽選手主要是宏任務(wù)代表 setTimeout 和微任務(wù)扛把子 Promise 及新貴 async/awiat

          基礎(chǔ)版本

          首先是單個(gè)任務(wù)的版本:

          setTimeout

          console.log('script start');
          setTimeout(() =>  {
            console.log('setTimeout1')
          }, 100)
          setTimeout(() =>  {
            console.log('setTimeout2')
          }, 50)
          console.log('script end');
          復(fù)制代碼

          在 Chrome 中有一個(gè) ProcessDelayTask 函數(shù),該函數(shù)會(huì)根據(jù)發(fā)起時(shí)間和延遲時(shí)間計(jì)算出到期的任務(wù),然后依次執(zhí)行這些到期的任務(wù)。執(zhí)行順序如下:

          image.png

          Promise

          console.log('script start');
          new Promise((resolve) =>  {
            console.log('promise1');
            resolve();
            console.log('promise1 end');
          }).then(() => {
            console.log('promise2');
          })
          console.log('script end');
          復(fù)制代碼

          Promise 內(nèi)部是 同步執(zhí)行 的,所以會(huì)有以下打印順序:

          image.png

          async/awiat

          async function async1() {
            console.log('async1 start')
            await async2();
            console.log('async1 end')
          }
          async function async2() {
            console.log('async2')
          }
          console.log('script start')
          async1();
          console.log('script end')
          復(fù)制代碼

          執(zhí)行順序如下,這里需要注意的是 async2 函數(shù)也是同步執(zhí)行的。

          image.png

          以上的基礎(chǔ)版本就是同步和異步的區(qū)別了,因?yàn)槭菃蝹€(gè)任務(wù),也沒有其它復(fù)雜的場(chǎng)景,在 Node.js 中的表現(xiàn)和瀏覽器是一樣的。

          組合版本一(setTimeoutPromise

          console.log('script start');
          setTimeout(() =>  {
            console.log('setimeout');
          }, 0)
          new Promise((resolve) =>  {
            console.log('promise1');
            resolve();
            console.log('promise1 end');
          }).then(() => {
            console.log('promise2');
          })
          console.log('script end');
          復(fù)制代碼

          在 Chrome 中的執(zhí)行順序:

          image.png

          這個(gè)版本就是簡單的宏任務(wù) setTimeout 和微任務(wù) Promise 的組合了,因?yàn)橐簿鸵粋€(gè)宏任務(wù)和一個(gè)微任務(wù),它在不同版本中的 Node.js 里面表現(xiàn)也是和在瀏覽器一樣的。

          組合版本二(setTimeoutPromiseasync/await

          console.log('script start')
          async function async1() {
            console.log('async1 start')
            await async2()
            console.log('async1 end')
          }
          async function async2() {
            console.log('async2')
          }
          async1()
          new Promise(resolve => {
            console.log('promise1 start')
            resolve()
            console.log('promise1 end')
          }).then(() => {
            console.log('promise2')
          })
          console.log('script end')
          復(fù)制代碼

          在 Chrome 中的執(zhí)行順序:

          image.png

          在 Node.js 10.13.0 中的執(zhí)行順序:

          image.png

          在 Node.js 12.18.3 中的執(zhí)行順序:

          image.png

          驚不驚喜!意不意外!Chrome(91版本)和 Node.js 12.18.3 的版本表現(xiàn)是一致的,在 Node.js 10.13.0 版本中有些差異,主要是 promise2async1 end 打印順序的不同,也就是說 async/await 在不同版本的 Node.js 處理是不一樣的(Chrome 70 后和 Chrome 70 前的處理也不一樣)。

          想要搞清楚其中的區(qū)別,可以去 promise, async, await, execution order[2] 一探究竟。

          混合版本一(setTimeoutPromise

          console.log('script start')
          setTimeout(() => {
            console.log('setTimeout1')
            Promise.resolve().then(() => {
              console.log('promise1')
            })
          }, 0)
          setTimeout(() => {
            console.log('setTimeout2')
            Promise.resolve().then(() => {
              console.log('promise2')
            })
          }, 0)
          console.log('script end')
          復(fù)制代碼

          在 Chrome 中的執(zhí)行順序:

          image.png

          在 Node.js 10.13.0 中的執(zhí)行順序:

          image.png

          在 Node.js 12.18.3 中的執(zhí)行順序:

          image.png

          這個(gè)混合版本主要是看 Node.js 版本 10 前后 Event Loop 的差別。

          混合版本二(setTimeoutPromise

          setTimeoutPromise

          console.log('script start')
          Promise.resolve().then(() => {
            console.log('promise1')
            setTimeout(() => {
              console.log('setTimeout1')
            }, 0)
          })
          setTimeout(() => {
            console.log('setTimeout2')
            Promise.resolve().then(() => {
              console.log('promise2')
            })
          }, 0)
          console.log('script end')
          復(fù)制代碼

          在 Chrome 中的執(zhí)行順序:

          image.png

          在 Node.js 10.13.0 中的執(zhí)行順序:

          image.png

          在 Node.js 12.18.3 中的執(zhí)行順序:

          image.png

          這個(gè)版本是執(zhí)行完同步代碼(script end)后,這時(shí)候第一個(gè) Promisepromise1) 在微任務(wù)隊(duì)列里面,第二個(gè) setTimeout 已經(jīng)加到消息隊(duì)列(延遲隊(duì)列)尾部了;這時(shí)候去執(zhí)行微任務(wù),即打印 promise1,然后把第一個(gè) setTimeout 加到消息隊(duì)列(延遲隊(duì)列)尾部,所以會(huì)是先打印 setTimeout2promise2 之后再打印 setTimeout1

          混合版本三(Promise

          宏任務(wù)和微任務(wù)的組合混合都差不多了,那來看看微任務(wù)之間的混合吧!

          async function async1() {
            console.log('async1 start');
            Promise.resolve(async2()).then(() => {
              console.log('async1 end');
            })
          }
          async function async2() {
            console.log('async2');
            Promise.resolve(async3()).then(() => {
              console.log('async2 end');
            })
          }
          async function async3() {
            console.log('async3');
            Promise.resolve().then(() => {
              console.log('async3 end');
            })
          }
          console.log('script start');
          async1();
          new Promise((resolve) =>  {
            console.log('promise1');
            resolve();
          }).then(() => {
            console.log('promise2');
          });
          console.log('script end');
          復(fù)制代碼

          在 Chrome 中的執(zhí)行順序:

          image.png

          在 Node.js 10.13.0 中的執(zhí)行順序:

          image.png

          這個(gè)也容易理解,按照調(diào)用棧的出入棧順序,先執(zhí)行的是同步代碼,執(zhí)行到 async3 的時(shí)候才把 async3 end 加入微任務(wù)隊(duì)列,之后 async3() 函數(shù)出棧,回到 async2,把 async2 end 加入微任務(wù)隊(duì)列,后面的同理,于是就有了這個(gè)打印順序。

          混合版本四(Promiseasync/await

          async function async1() {
            console.log('async1 start');
            await async2();
            console.log('async1 end');
          }
          async function async2() {
            console.log('async2');
            await async3()
            console.log('async2 end')
          }
          async function async3() {
            await console.log('async3');
            console.log('async3 end')
          }
          console.log('script start');
          async1();
          new Promise((resolve) => {
            console.log('promise1');
            resolve();
          }).then(() => {
            console.log('promise2');
          });
          console.log('script end');
          復(fù)制代碼

          在 Chrome 中的執(zhí)行順序:

          image.png

          在 Node.js 10.13.0 中的執(zhí)行順序:

          image.png

          版本四和版本三的區(qū)別是將 async 函數(shù)里面的 Promise 換成了 await,而且版本三中的 async 函數(shù)里面沒有 await,都可以把 function 前面的 async 標(biāo)記拿掉的,不要被迷惑了哦。

          在同步代碼執(zhí)行到 script end 之前我想都沒什么問題,async 里面都是有 await 的,所以在后面的代碼可以理解為如下形式:

          Promise.resolve().then(() => {
            console.log('async3 end');
            Promise.resolve().then(() => {
              console.log('async2 end');
              Promise.resolve().then(() => {
                console.log('async1 end');
              })
            })
          })
          Promise.resolve().then(() => {
            console.log('promise2');
          })
          復(fù)制代碼

          混合版本五(Promiseasync/await

          function async1() {
            console.log('async1 start');
            Promise.resolve(async2()).then(() => {
              console.log('async1 end');
            })
          }
          function async2() {
            console.log('async2');
            Promise.resolve(async3()).then(() => {
              console.log('async2 end');
            })
          }
          async function async3() {
            await console.log('async3');
            console.log('async3 end');
          }
          console.log('script start');
          async1();
          new Promise(function (resolve{
            console.log('promise1');
            resolve();
          }).then(function () {
            console.log('promise2');
          });
          console.log('script end');
          復(fù)制代碼

          在 Chrome 中的執(zhí)行順序:

          image.png

          在 Node.js 10.13.0 中的執(zhí)行順序:

          image.png

          通過前面版本四的考驗(yàn),這里應(yīng)該也難不倒你,這里只有 async3 里面有一個(gè) await,執(zhí)行到 script end 之前都一樣,到了 async3 end 后面的代碼可以轉(zhuǎn)化為以下形式思考:

          Promise.resolve().then(() => {
            console.log('async3 end');
            Promise.resolve().then(() => {
              console.log('async2 end');
            })
          })
          Promise.resolve().then(() => {
            console.log('async1 end');
          })
          Promise.resolve().then(() => {
            console.log('promise2');
          })
          復(fù)制代碼

          總結(jié)

          紙上得來終覺淺,絕知此事要躬行。

          關(guān)于本文

          來源:起風(fēng)了Q

          https://juejin.cn/post/6979244116199047204


          1. JavaScript 重溫系列(22篇全)
          2. ECMAScript 重溫系列(10篇全)
          3. JavaScript設(shè)計(jì)模式 重溫系列(9篇全)
          4. 正則 / 框架 / 算法等 重溫系列(16篇全)
          5. Webpack4 入門(上)|| Webpack4 入門(下)
          6. MobX 入門(上) ||  MobX 入門(下)
          7. 120+篇原創(chuàng)系列匯總

          回復(fù)“加群”與大佬們一起交流學(xué)習(xí)~

          點(diǎn)擊“閱讀原文”查看 120+ 篇原創(chuàng)文章

          瀏覽 73
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

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

          手機(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Ⅴ大片在线直播 | 永久官看美女裸体网站 | 国产一区二区天堂 | 国产麻豆成人 |