Generator和async

什麼是Generator函數

Generator函數與普通函數的區別是該函數可以分步驟阻塞,不像普通函數需要一路走到底,就像是Generator生成一堆的小函數,只有主動調用next()纔會一個個的執行這些小函數。總結起來就是Generator函數中間可以停下來,可以使用yield來暫時的放棄執行。

拿一個形象的例子:普通函數好比是坐高鐵或者乘坐飛機,我們只有到達目的地了,才停止下來,中間是不允許有停歇的,但是Generator函數好比是乘坐計程車,當我們需要在某個地方去做什麼事,比如上個廁所,是可以叫師傅在某地方等一會,回來了再繼續趕往目的地

yield的理解與使用

Generator是一個狀態機,封裝了多個內部狀態,執行 Generator 函數會返回一個遍歷器對象,返回的遍歷器對象,可以使用next依次遍歷Generator函數內部的每一個狀態。Generator函數是分段執行的,yield表達式是暫停執行的標記,而next方法可以恢復執行。Generator 函數的執行必須靠執行器。

yield可以傳參也可以返回內容(一般的返回的是上一步執行的結果)。一個yield就是一堵牆,牆的兩面都是小的函數,只有牆的一面的函數執行完了,纔可以接着執行另一面的函數。如下圖幫助理解:
在這裏插入圖片描述

yield傳參的形式與意義,如下圖:
在這裏插入圖片描述
Generatoryield綜合使用,如下僞代碼:
在這裏插入圖片描述

Generator相比Promise的優勢

Promise也是處理異步的,但爲什麼會再出現generator,是因爲當我們使用promise的時候,如果中間需要有一些邏輯的判斷,則這樣寫出來的promise代碼相比普通的函數而言是沒有任何的優勢的。但是如果使用generator可以簡化很多,並且是有序,正確的執行 每道工序。

總結:promise適合處理一次讀一堆的異步操作,而generator適合在讀的過程中有一些邏輯的處理,分批,有順序的處理。簡單的來說是generator是對promise的封裝

async函數

async函數是Generator函數的語法糖,將Generator的星號換成asyncyield換成awaitasync函數比Generator函數更好用

  • 自帶執行器,執行起來,跟調用普通函數一樣
  • asyncawait 語義更清晰,async表示函數裏有異步操作,await 表示緊跟在後面的表達式需要等待結果
  • await後面啥都可以跟,可以是Promise 也可以是對象和原始類型的值(數值、字符串和布爾值,但這時等同於同步操作
  • async函數的 返回值是 Promise
    正常情況下,await命令是個Promise對象,如果不是 會被轉成一個 立即resolved的對象,async函數完全可以看作多個異步操作,包裝成的一個Promise對象(因爲await函數返回的是Promise對象),而await命令就是內部then命令的語法糖。
    然而,然而,我們沒寫錯誤處理。
async function f() {
  return 'hello world';
}
f().then().catch()

正常情況下 async 函數中return結果會使Promise對象變爲 resolved狀態,返回值作爲then方法回調函數的參數,而出錯則會使Promise對象的變爲reject狀態,錯誤會被catch捕獲。

因爲 async函數 相當於對 多個Promise的封裝,所以必須等到內部所有的await命令執行完,纔會改變自己的狀態爲resolved,除非 碰到return語句或者拋出了異常。
也就是說,正常情況下 只有async函數內部的異步操作執行完,纔會執行then後面的語句
只要一個await後面的Promise變爲rejected,整個async函數就會中斷執行,整個async返回的Promise對象就會是rejected狀態

async function f() {
  await Promise.reject('出錯了');
  await Promise.resolve('hello world'); // 不會執行
}

因爲第一個await後面的對象reject了,所以整個async函數就中斷執行了
有時,我們希望即使前一個異步操作失敗,也不要中斷後面的異步操作。
這時可以將第一個await放在try…catch結構裏面,這樣不管這個異步操作是否成功,第二個await都會執行。
await命令後面的Promise對象,運行結果可能是rejected,所以最好把await命令放在try…catch代碼塊中。

try catch

try catch是JavaScript的異常處理機制,把可能出錯的代碼放在try語句塊中,如果出錯了,就會被catch捕獲來處理異常。如果不catch 一旦出錯就會造成程序崩潰。

如果有多個await命令,可以將其都放在try catch結構中,如果執行出錯,catch會去捕獲異常

async function f() {
    try {
     await Promise.reject('出錯了');
     console.log('上面已經出錯了');
     return await Promise.resolve('hello world');
    } catch(e) {
        console.log(e);
    }
  }
  
  f()
  .then(v => console.log(v))

catch會去捕獲try代碼塊中的錯誤,只要有一個拋出了異常,就不會繼續執行,所以上面的代碼不會打印上面已經出錯了也不會執行return await Promise.resolve(‘hello world’);
因爲使用了trycatch 所以 async 是順利執行完成的,其中的報錯 被 try catch處理了,所以異常不會被async返回的Promise的catch捕獲,因此async返回的Promise對象狀態是resolved

如果異步函數沒有依賴關係,最好併發執行

await 會等待後面的異步操作執行完畢,纔會繼續執行

let foo = await getFoo();
let bar = await getBar();

上面的代碼會順序執行,
如果需要多個await沒有相互依賴,最好讓他們同時觸發,可以使用以下兩種方式:

使用Promise.all() 包裝一個新的Promise對象

let [foo, bar] = await Promise.all([getFoo(), getBar()]);

不等待分別執行,返回新的Promise對象
//沒用await 立即執行返回 Promise對象

let fooPromise = getFoo();
let barPromise = getBar();

// 等待 Promise對象的結果 之前也說過 await就像是then的語法糖
let foo = await fooPromise;
let bar = await barPromise;

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章