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

          你不知道的 async、await 魔鬼細(xì)節(jié)

          共 19811字,需瀏覽 40分鐘

           ·

          2024-08-09 08:45


          0、前言

          關(guān)于promise、async/await的使用相信很多小伙伴都比較熟悉了,但是提到事件循環(huán)機(jī)制輸出結(jié)果類似的題目,你敢說(shuō)都會(huì)?

          試一試?

          ??1:

          async function async1 ({
              await new Promise((resolve, reject) => {
                  resolve()
              })
              console.log('A')
          }

          async1()

          new Promise((resolve) => {
              console.log('B')
              resolve()
          }).then(() => {
              console.log('C')
          }).then(() => {
              console.log('D')
          })

          // 最終結(jié)果??: B A C D

          ??2:

          async function async1 ({
              await async2()
              console.log('A')
          }

          async function async2 ({
              return new Promise((resolve, reject) => {
                  resolve()
              })
          }

          async1()

          new Promise((resolve) => {
              console.log('B')
              resolve()
          }).then(() => {
              console.log('C')
          }).then(() => {
              console.log('D')
          })

          // 最終結(jié)果??: B C D A

          ?基本一樣的代碼為什么會(huì)出現(xiàn)差別,話不多說(shuō)??

          1、async 函數(shù)返回值

          在討論 await 之前,先聊一下 async 函數(shù)處理返回值的問(wèn)題,它會(huì)像 Promise.prototype.then 一樣,會(huì)對(duì)返回值的類型進(jìn)行辨識(shí)。

          ??根據(jù)返回值的類型,引起 js引擎 對(duì)返回值處理方式的不同

          ??結(jié)論:async函數(shù)在拋出返回值時(shí),會(huì)根據(jù)返回值類型開(kāi)啟不同數(shù)目的微任務(wù)

          • return結(jié)果值:非thenable、非promise(不等待)
          • return結(jié)果值:thenable(等待 1個(gè)then的時(shí)間)
          • return結(jié)果值:promise(等待 2個(gè)then的時(shí)間)

          ??1:

          async function testA ({
              return 1;
          }

          testA().then(() => console.log(1));
          Promise.resolve()
              .then(() => console.log(2))
              .then(() => console.log(3));

          // (不等待)最終結(jié)果??: 1 2 3

          ??2:

          async function testB ({
              return {
                  then (cb) {
                      cb();
                  }
              };
          }

          testB().then(() => console.log(1));
          Promise.resolve()
              .then(() => console.log(2))
              .then(() => console.log(3));

          // (等待一個(gè)then)最終結(jié)果??: 2 1 3

          ??3:

          async function testC ({
              return new Promise((resolve, reject) => {
                  resolve()
              })
          }

          testC().then(() => console.log(1));
          Promise.resolve()
              .then(() => console.log(2))
              .then(() => console.log(3));
              
          // (等待兩個(gè)then)最終結(jié)果??: 2 3 1




          async function testC ({
              return new Promise((resolve, reject) => {
                  resolve()
              })


          testC().then(() => console.log(1));
          Promise.resolve()
              .then(() => console.log(2))
              .then(() => console.log(3))
              .then(() => console.log(4))

          // (等待兩個(gè)then)最終結(jié)果??: 2 3 1 4

          看了這三個(gè)??是不是對(duì)上面的結(jié)論有了更深的認(rèn)識(shí)?

          稍安勿躁,來(lái)試試一個(gè)經(jīng)典面試題??

          async function async1 ({
              console.log('1')
              await async2()
              console.log('AAA')
          }

          async function async2 ({
              console.log('3')
              return new Promise((resolve, reject) => {
                  resolve()
                  console.log('4')
              })
          }

          console.log('5')

          setTimeout(() => {
              console.log('6')
          }, 0);

          async1()

          new Promise((resolve) => {
              console.log('7')
              resolve()
          }).then(() => {
              console.log('8')
          }).then(() => {
              console.log('9')
          }).then(() => {
              console.log('10')
          })
          console.log('11')

          // 最終結(jié)果??: 5 1 3 4 7 11 8 9 AAA 10 6

          ??做錯(cuò)了吧?

          哈哈沒(méi)關(guān)系

          步驟拆分??:

          1. 先執(zhí)行同步代碼,輸出5

          2. 執(zhí)行setTimeout,是放入宏任務(wù)異步隊(duì)列中

          3. 接著執(zhí)行async1函數(shù),輸出1

          4. 執(zhí)行async2函數(shù),輸出3

          5. Promise構(gòu)造器中代碼屬于同步代碼,輸出4

            async2函數(shù)的返回值是Promise,等待2個(gè)then后放行,所以AAA暫時(shí)無(wú)法輸出

          6. async1函數(shù)暫時(shí)結(jié)束,繼續(xù)往下走,輸出7

          7. 同步代碼,輸出11

          8. 執(zhí)行第一個(gè)then,輸出8

          9. 執(zhí)行第二個(gè)then,輸出9

          10. 終于到了兩個(gè)then執(zhí)行完畢,執(zhí)行async1函數(shù)里面剩下的,輸出AAA

          11. 再執(zhí)行最后一個(gè)微任務(wù)then,輸出10

          12. 執(zhí)行最后的宏任務(wù)setTimeout,輸出6

          ?是不是豁然開(kāi)朗,歡迎點(diǎn)贊收藏!

          2、await 右值類型區(qū)別

          2.1、非 thenable

          ??1:

          async function test ({
              console.log(1);
              await 1;
              console.log(2);
          }

          test();
          console.log(3);
          // 最終結(jié)果??: 1 3 2

          ??2:

          function func ({
              console.log(2);
          }

          async function test ({
              console.log(1);
              await func();
              console.log(3);
          }

          test();
          console.log(4);

          // 最終結(jié)果??: 1 2 4 3

          ??3:

          async function test ({
              console.log(1);
              await 123
              console.log(2);
          }

          test();
          console.log(3);

          Promise.resolve()
              .then(() => console.log(4))
              .then(() => console.log(5))
              .then(() => console.log(6))
              .then(() => console.log(7));

          // 最終結(jié)果??: 1 3 2 4 5 6 7

          Note:

          await后面接非 thenable 類型,會(huì)立即向微任務(wù)隊(duì)列添加一個(gè)微任務(wù)then但不需等待

          2.2、thenable類型

          async function test ({
              console.log(1);
              await {
                  then (cb) {
                      cb();
                  },
              };
              console.log(2);
          }

          test();
          console.log(3);

          Promise.resolve()
              .then(() => console.log(4))
              .then(() => console.log(5))
              .then(() => console.log(6))
              .then(() => console.log(7));

          // 最終結(jié)果??: 1 3 4 2 5 6 7

          Note:

          await 后面接 thenable 類型,需要等待一個(gè) then 的時(shí)間之后執(zhí)行

          2.3、Promise類型

          async function test ({
              console.log(1);
              await new Promise((resolve, reject) => {
                  resolve()
              })
              console.log(2);
          }

          test();
          console.log(3);

          Promise.resolve()
              .then(() => console.log(4))
              .then(() => console.log(5))
              .then(() => console.log(6))
              .then(() => console.log(7));

          // 最終結(jié)果??: 1 3 2 4 5 6 7

          ?為什么表現(xiàn)的和非 thenable 值一樣呢?為什么不等待兩個(gè) then 的時(shí)間呢?

          Note:

          • TC 39(ECMAScript標(biāo)準(zhǔn)制定者) 對(duì)await 后面是 promise 的情況如何處理進(jìn)行了一次修改,移除了額外的兩個(gè)微任務(wù),在早期版本,依然會(huì)等待兩個(gè) then 的時(shí)間
          • 有大佬翻譯了官方解釋:更快的 async 函數(shù)和 promises[1],但在這次更新中并沒(méi)有修改 thenable 的情況

          這樣做可以極大的優(yōu)化 await 等待的速度??

          async function func ({
              console.log(1);
              await 1;
              console.log(2);
              await 2;
              console.log(3);
              await 3;
              console.log(4);
          }

          async function test ({
              console.log(5);
              await func();
              console.log(6);
          }

          test();
          console.log(7);

          Promise.resolve()
              .then(() => console.log(8))
              .then(() => console.log(9))
              .then(() => console.log(10))
              .then(() => console.log(11));

          // 最終結(jié)果??: 5 1 7 2 8 3 9 4 10 6 11

          Note:

          awaitPromise.prototype.then 雖然很多時(shí)候可以在時(shí)間順序上能等效,但是它們之間有本質(zhì)的區(qū)別

          • test 函數(shù)中的 await 會(huì)等待 func 函數(shù)中所有的 await 取得 恢復(fù)函數(shù)執(zhí)行 的命令并且整個(gè)函數(shù)執(zhí)行完畢后才能獲得取得 恢復(fù)函數(shù)執(zhí)行的命令;
          • 也就是說(shuō),func 函數(shù)的 await 此時(shí)不能在時(shí)間的順序上等效 then,而要等待到 test 函數(shù)完全執(zhí)行完畢;
          • 比如這里的數(shù)字6很晚才輸出,如果單純看成then的話,在下一個(gè)微任務(wù)隊(duì)列執(zhí)行時(shí)6就應(yīng)該作為同步代碼輸出了才對(duì)。

          所以我們可以合并兩個(gè)函數(shù)的代碼??

          async function test ({
              console.log(5);

              console.log(1);
              await 1;
              console.log(2);
              await 2;
              console.log(3);
              await 3;
              console.log(4);
              await null;
              
              console.log(6);
          }

          test();
          console.log(7);

          Promise.resolve()
              .then(() => console.log(8))
              .then(() => console.log(9))
              .then(() => console.log(10))
              .then(() => console.log(11));

          // 最終結(jié)果??: 5 1 7 2 8 3 9 4 10 6 11


          因?yàn)閷⒃镜暮瘮?shù)融合,此時(shí)的 await 可以等效為 Promise.prototype.then,又完全可以等效如下代碼??

          async function test ({
              console.log(5);
              console.log(1);
              Promise.resolve()
                  .then(() => console.log(2))
                  .then(() => console.log(3))
                  .then(() => console.log(4))
                  .then(() => console.log(6))
          }

          test();
          console.log(7);

          Promise.resolve()
              .then(() => console.log(8))
              .then(() => console.log(9))
              .then(() => console.log(10))
              .then(() => console.log(11));

          // 最終結(jié)果??: 5 1 7 2 8 3 9 4 10 6 11


          以上三種寫法在時(shí)間的順序上完全等效,所以你 完全可以將 await 后面的代碼可以看做在 then 里面執(zhí)行的結(jié)果,又因?yàn)?async 函數(shù)會(huì)返回 promise 實(shí)例,所以還可以等效成??

          async function test ({
              console.log(5);
              console.log(1);
          }

          test()
              .then(() => console.log(2))
              .then(() => console.log(3))
              .then(() => console.log(4))
              .then(() => console.log(6))

          console.log(7);

          Promise.resolve()
              .then(() => console.log(8))
              .then(() => console.log(9))
              .then(() => console.log(10))
              .then(() => console.log(11));

          // 最終結(jié)果??: 5 1 7 2 8 3 9 4 10 6 11

          可以發(fā)現(xiàn),test 函數(shù)全是走的同步代碼...

          所以??:**async/await 是用同步的方式,執(zhí)行異步操作**

          3、??

          ??1:

          async function async2 ({
              new Promise((resolve, reject) => {
                  resolve()
              })
          }

          async function async3 ({
              return new Promise((resolve, reject) => {
                  resolve()
              })
          }

          async function async1 ({
              // 方式一:最終結(jié)果:B A C D
              // await new Promise((resolve, reject) => {
              //     resolve()
              // })

              // 方式二:最終結(jié)果:B A C D
              // await async2()

              // 方式三:最終結(jié)果:B C D A
              await async3()

              console.log('A')
          }

          async1()

          new Promise((resolve) => {
              console.log('B')
              resolve()
          }).then(() => {
              console.log('C')
          }).then(() => {
              console.log('D')
          })

          大致思路??:

          • 首先,**async函數(shù)的整體返回值永遠(yuǎn)都是Promise,無(wú)論值本身是什么**
          • 方式一:await的是Promise,無(wú)需等待
          • 方式二:await的是async函數(shù),但是該函數(shù)的返回值本身是**非thenable**,無(wú)需等待
          • 方式三:await的是async函數(shù),且返回值本身是Promise,需等待兩個(gè)then時(shí)間

          ??2:

          function func () {
          console.log(2);

          // 方式一:1 2 4 5 3 6 7
          // Promise.resolve()
          // .then(() => console.log(5))
          // .then(() => console.log(6))
          // .then(() => console.log(7))

          // 方式二:1 2 4 5 6 7 3
          return Promise.resolve()
          .then(() => console.log(5))
          .then(() => console.log(6))
          .then(() => console.log(7))
          }

          async function test () {
          console.log(1);
          await func();
          console.log(3);
          }

          test();
          console.log(4);

          步驟拆分??:

          • 方式一:

            • 同步代碼輸出1、2,接著將log(5)處的then1加入微任務(wù)隊(duì)列,await拿到確切的func函數(shù)返回值undefined,將后續(xù)代碼放入微任務(wù)隊(duì)列(then2,可以這樣理解)
            • 執(zhí)行同步代碼輸出4,到此,所有同步代碼完畢
            • 執(zhí)行第一個(gè)放入的微任務(wù)then1輸出5,產(chǎn)生log(6)的微任務(wù)then3
            • 執(zhí)行第二個(gè)放入的微任務(wù)then2輸出3
            • 然后執(zhí)行微任務(wù)then3,輸出6,產(chǎn)生log(7)的微任務(wù)then4
            • 執(zhí)行then4,輸出7
          • 方式二:

            • 同步代碼輸出1、2await拿到func函數(shù)返回值,但是并未獲得具體的結(jié)果(由Promise本身機(jī)制決定),暫停執(zhí)行當(dāng)前async函數(shù)內(nèi)的代碼(跳出、讓行)
            • 輸出4,到此,所有同步代碼完畢
            • await一直等到Promise.resolve().then...執(zhí)行完成,再放行輸出3

          方式二沒(méi)太明白?

          繼續(xù)??

          function func () {
          console.log(2);

          return Promise.resolve()
          .then(() => console.log(5))
          .then(() => console.log(6))
          .then(() => console.log(7))
          }

          async function test () {
          console.log(1);
          await func()
          console.log(3);
          }

          test();
          console.log(4);

          new Promise((resolve) => {
          console.log('B')
          resolve()
          }).then(() => {
          console.log('C')
          }).then(() => {
          console.log('D')
          })

          // 最終結(jié)果??: 1 2 4 B 5 C 6 D 7 3

          還是沒(méi)懂?

          繼續(xù)??

          async function test ({
              console.log(1);
              await Promise.resolve()
                  .then(() => console.log(5))
                  .then(() => console.log(6))
                  .then(() => console.log(7))
              console.log(3);
          }

          test();
          console.log(4);

          new Promise((resolve) => {
              console.log('B')
              resolve()
          }).then(() => {
              console.log('C')
          }).then(() => {
              console.log('D')
          })

          // 最終結(jié)果??: 1 4    B 5 C 6 D 7 3

          Note:

          綜上,await一定要等到右側(cè)的表達(dá)式有確切的值才會(huì)放行,否則將一直等待(阻塞當(dāng)前async函數(shù)內(nèi)的后續(xù)代碼),不服看看這個(gè)??

          • function func ({
              return new Promise((resolve) => {
                  console.log('B')
                  // resolve() 故意一直保持pending
              })
            }

            async function test ({
              console.log(1);
              await func()
              console.log(3);
            }

            test();
            console.log(4);
            // 最終結(jié)果??: 1 B 4 (永遠(yuǎn)不會(huì)打印3)


            // ---------------------或者寫為??-------------------
            async function test ({
              console.log(1);
              await new Promise((resolve) => {
                  console.log('B')
                  // resolve() 故意一直保持pending
              })
              console.log(3);
            }

            test();
            console.log(4);
            // 最終結(jié)果??: 1 B 4 (永遠(yuǎn)不會(huì)打印3)

          ??3:

          async function func ({
              console.log(2);
              return {
                  then (cb) {
                      cb()
                  }
              }
          }

          async function test ({
              console.log(1);
              await func();
              console.log(3);
          }

          test();
          console.log(4);

          new Promise((resolve) => {
              console.log('B')
              resolve()
          }).then(() => {
              console.log('C')
          }).then(() => {
              console.log('D')
          })

          // 最終結(jié)果??: 1 2 4 B C 3 D

          步驟拆分??:

          • 同步代碼輸出1、2
          • await拿到func函數(shù)的具體返回值thenable,將當(dāng)前async函數(shù)內(nèi)的后續(xù)代碼放入微任務(wù)then1(但是需要等待一個(gè)then時(shí)間)
          • 同步代碼輸出4、B,產(chǎn)生log(C)的微任務(wù)then2
          • 由于then1滯后一個(gè)then時(shí)間,直接執(zhí)行then2輸出C,產(chǎn)生log(D)的微任務(wù)then3
          • 執(zhí)行原本滯后一個(gè)then時(shí)間的微任務(wù)then1,輸出3
          • 執(zhí)行最后一個(gè)微任務(wù)then3輸出D

          4、總結(jié)

          async函數(shù)返回值

          • ??結(jié)論:async函數(shù)在拋出返回值時(shí),會(huì)根據(jù)返回值類型開(kāi)啟不同數(shù)目的微任務(wù)

            • return結(jié)果值:非thenable、非promise(不等待)
            • return結(jié)果值:thenable(等待 1個(gè)then的時(shí)間)
            • return結(jié)果值:promise(等待 2個(gè)then的時(shí)間)

          await右值類型區(qū)別

          • 接非 thenable 類型,會(huì)立即向微任務(wù)隊(duì)列添加一個(gè)微任務(wù)then但不需等待

          • thenable 類型,需要等待一個(gè) then 的時(shí)間之后執(zhí)行

          • Promise類型(有確定的返回值),會(huì)立即向微任務(wù)隊(duì)列添加一個(gè)微任務(wù)then但不需等待

            • TC 39 對(duì)await 后面是 promise 的情況如何處理進(jìn)行了一次修改,移除了額外的兩個(gè)微任務(wù),在早期版本,依然會(huì)等待兩個(gè) then 的時(shí)間

          參考資料

          [1]

          https://juejin.cn/post/6844903715342647310#heading-3: https://juejin.cn/post/6844903715342647310#heading-3

          作者: Squirrel_

          https://juejin.cn/post/7194744938276323384


          瀏覽 117
          點(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>
                  国产成人三级在线观看 | 午夜电影无码 | 乱轮视频网站 | 中文无码一区二区三区. | 97超碰人人模人人爱 |