ES6問答-async函數

async函數

  1. async是什麼?它和Genernator函數外觀上的區別是什麼?javascript

    const asyncReadFile = async function(){
        const f1 = await readFile(url1)
        const f2 = await readFile(url2)
    }

    async是Genernator函數的語法糖。java

    asyncGenernator的區別有兩點:promise

    • Genernator*變成async
    • yield變成 await
  2. asyncGenernator作了哪些改進?併發

    • 內置執行器。

      執行時和普通函數同樣,只須要調用函數就完了,不須要 next()方法異步

    • 更好的語義。

      async表示它後面的函數裏有異步操做;await表示緊跟在後面的表達式須要等待結果async

    • 更廣的適用性。

      await後面能夠Genernator, Promise對象和原始類型的值。(原始類型的值會被自動轉化爲resolved狀態的Promise對象)函數

    • 返回值是Promise

      async函數返回的是Promise對象,方便用 then進行下一步操做fetch

    • 總的來講:async函數可當作多個異步操做,包裝成一個Promise對象,而await命令就是內部then命令的語法糖。
  3. async執行的例子:this

    function timeout(ms){
      return new Promise((resolve) => {
        setTimeout(resolve, ms)
      })
    }
    async function asyncPrint(value, ms){
      await timeout(ms);
      console.log(value)
    }
    
    asyncPrint("你好啊", 5000)

    5s以後打印 「你好啊」 url

    以上 asyncPrint的意思是,等待timeout函數執行完了以後,纔會繼續執行 console.log(value)

  4. async函數的幾種使用場景?

    五種場景下:

    • 函數聲明

      async function foo(){}
    • 函數表達式

      const foo = async function(){}
    • 箭頭函數

      const foo = async () => {}
    • 對象的方法

      let obj = {
       async foo(){
      
           }
      }
      obj.foo().then(...)
    • class方法

      class Storage{
       constructor(){
              this.cachePromise = caches.open("avatars")
          }
          async getAvatar(name){
              const cache = await this.cachePromise;
              return cache.match()
          }
      }
      const storage  = new Storage()
      storage.getAvatar("joy").then(...)
  5. async函數返回什麼?

    返回一個 Promise對象。

    async函數內部 return語句的返回值,會成爲then方法回調函數的參數。

    async函數內部拋出的錯誤,會致使返回的Promise對象變爲 rejected狀態

  6. `async函數中Promise對象是如何變化的?

    async函數返回的是Promise對象P。必須等到內部 await命令的Promise對象執行完後,P纔會發生狀態改變,除非遇到return語句,或者拋出了錯誤。

    換言之,async函數 內部的異步操做執行完了,纔會執行調用它時後面then方法指定的回調函數。

  7. await命令的返回值是什麼?

    • 正常狀況下,await後面是Promise對象,會返回此對象的結果;若是不是Promise對象,直接返回對應的值
    • await命令後面跟了 thenable對象,會把 thenable對象當作 Promise對象來處理
  8. await後面的Promise對象若是變爲 rejected會怎樣?

    • rejected的參數會被async函數catch方法的回調函數接收到。

      async function f() {
        await Promise.reject('出錯了');
      }
      
      f()
      .then(v => console.log(v))
      .catch(e => console.log(e))
    • 任何一個await語句的Promise對象變爲reject狀態,整個async函數都會被中斷

      async function f() {
        await Promise.reject('出錯了');
        await Promise.resolve('hello world'); // 不會執行
      }
  9. 若是但願前一個異步操做失敗,不中斷後面的異步操做,怎麼處理?

    兩種方法:

    • 把第一個await放到 try...catch裏面

      async function f(){
          try{
              await Promise.reject("出錯了")
          }catch(e){}
          return await Promise.resolve("hello world")
      }
      f().then(v =>console.log(v)).catch(e => console.log(e))
      // 打印的是  hello world
    • await後面的Promise對象再跟一個 catch方法,處理前面可能出現的錯誤

      async function f(){
        await Promise.reject("出錯啦").catch(e => {
          console.log("await 內部promise被reject了")
        })
        return await Promise.resolve("hello world");
      }
      f().then(v => console.log(v)).catch(e => console.log(e))

      打印的內容是:

      await 內部promise被reject了
      hello world

  10. await後面的異步操做出錯了(例如某行代碼throw 了一個Error),是什麼意思?

    async函數返回的Promise對象被reject

    async function f() {
      await new Promise(function (resolve, reject) {
        throw new Error('出錯了');
      });
    }
    
    f()
    .then(v => console.log(v))
    .catch(e => console.log(e)) // catch執行了, e就是拋出的錯誤對象 new Error('出錯了')
  11. 如何防止出錯呢?

    仍是將其放到try{ }catch(e){ }代碼塊中。

    若是有多個await命令,能夠將其統一放到try{ }catch(e){ }結構裏。
    下面是實際例子: 屢次發起客戶端請求,若是請求成功,跳出循環往下執行;若是不成功,繼續請求,直到達到最大數目 NUM_RETRIES

    async function test(){
      let i;
      for(i = 0; i < NUM_RETRIES; ++i){
        try{
          await superagent.get(url)
          break;
        }catch(err){}
      }
      console.log(i)
    }
    test()

    若是await操做成功,則會break,跳出for循環;若是await操做不成功,則會被catch住,而後繼續下一輪for循環,直到超過 NUM_RETRIES或者 await操做成功。

  12. asyncawait有哪些使用上注意的點?

    • await命令後的Promise對象可能reject,所以await命令最好放在try{ }catch(e){ }代碼塊中

      async function myFunction() {
        try {
          await somethingThatReturnsAPromise();
        } catch (err) {
          console.log(err);
        }
      }
      
      // 另外一種寫法
      
      async function myFunction() {
        await somethingThatReturnsAPromise()
        .catch(function (err) {
          console.log(err);
        });
      }
    • 多個await異步操做時,若是不存在繼發關係,讓它們同時觸發比較好

      能夠結合 Promise.all方法

      // 寫法一
      let [foo, bar] = await Promise.all([getFoo(), getBar()]);
      
      // 寫法二
      let fooPromise = getFoo();
      let barPromise = getBar();
      let foo = await fooPromise;
      let bar = await barPromise;
    • await命令只能用在async函數中,用在普通函數裏會報錯

      注意forEach的回調函數,await也不能出如今回調函數裏

    • async函數能夠保留運行堆棧

      看例子:

      const a = () => {
          b().then(() => c()) ;
      }
      
      const a = async () => {
          await b();
          c();
      }

      上面的例子中,b運行時,a可能已經執行完了。若是此時b或c報錯,錯誤堆棧將不包括a

      下面例子中,b運行時,a只是暫停,若此時b或者c報錯了,錯誤堆棧中將包括a

  13. Promise寫法和Genernator寫法,async有什麼好處?

    Promise寫法有不少catchthen,語義性不強。

    Genernator函數須要有一個任務運行器,自動執行Genernator函數,而且 yield後面的表達式,必須返回Promise對象

    async最簡潔,最符合語義,將Genernator寫法的自動執行器,改在 語言層面提供,不暴露給用戶,代碼量最少。

    async function chainAnimationAsync(elem, ainmations){
        let ret = null
        try{
            for(letanim of animations){
                ret = await anim(elem);
            }
        }catch(e){}
        return ret
    }
  14. async的實例: 按順序完成異步操做——依次遠程讀取一組URL,而後按照讀取順序輸出結果

    async function logInOrder(urls){
      const textPromises = urls.map(async url => {
        const response = await fetch(url)
        return response.text()
      })
      for(const textPromise of textPromises){
        console.log(await textPromise)
      }
    }

    map的參數是async函數。這幾個async是併發的。只有async函數內部纔是繼發的【const response = await fetch(url)return response.text() 先執行】,外部並不受影響。

    後面在 for...of循環內部使用了 await,這幾個await是順序執行。

相關文章
相關標籤/搜索