開發與維運

ES7中【async…await…】講解

上一章我們說過了Promise,其實使用上已經足夠。就是在理解上,還需要自己多動手敲一些代碼;今天來說說另一個異步請求,是ES7中的新方法async...await...

那麼我們知道了異步請求的優勢,也知道可以使用回調函數或者更好的方法Promise來處理異步,那麼為什麼還要有引入新的異步方法?

答案是Promise也有缺點:
首先Promise的代碼還達不到很簡潔的程度(如果有太複雜的異步,代碼也會很複雜,可讀性不高);
其次不能像同步代碼一樣方便獲取到錯誤信息;

那麼async/await怎麼用,我們從字面意思理解:async:異步;await:等待

async 是異步的意思,是一個關鍵字,將 async 關鍵字放在函數前邊,表示某個函數就是一個異步函數,這樣就不會影響下邊線程的執行順序,那麼為什麼有這個關鍵字就會變成異步請求?我們打印一下帶有關鍵字的函數看一下:

async function foo(n){
    return n
  }
console.log(foo(222))

控制檯會打印出這樣:

image

會神奇的發現,打印出來的是一個帶有Promise對象,並且resolved完成狀態,結果返回了222。

所以,async函數是包裝成了Promise對象作為的返回值 => Promise.resolve(value)

await是等待的意思,放在要異步操作的方法前邊。在異步函數當中,等待一段要發生的代碼,返回後再向下執行,在沒返回值之前,當前異步函數中await後的代碼暫不執行。我們看兩個重要的例子,仔細看一下

// 隨便寫一個延遲函數
function result() {
    setTimeout(() => {
        console.log('異步請求')
    }, 2000);
}

// 然後使用這個方法,先用一個await
async function foo(){
    console.log('111')
    await result();
    console.log('222');
  }
foo()
console.log('333')

image

打印結果如上,函數照常執行,所以先打印了111,然後遇到了await等待關鍵字,發起了異步請求,所以222是在333之後打印的,在等待2秒之後,最後打印出“異步請求”。

那麼如果我把await關鍵字去掉會怎麼樣?

// 隨便寫一個延遲函數
function result() {
    setTimeout(() => {
        console.log('異步請求')
    }, 2000);
}

// 然後使用這個方法,先用一個await
async function foo(){
    console.log('111')
    result();
    console.log('222');
  }
foo()
console.log('333')

結果是:

image

可以看出,222不再受到await影響,按照了111、222、333正常打印。

問題來了:

  1. 那await加與不加有什麼用呢?2秒鐘後不都可以打印出“異步請求”四個字嗎?
  2. 上邊所說的 “ 在異步函數當中,await等待一段要發生的代碼,返回後再向下執行,在沒返回值之前,當前異步函數中await後的代碼暫不執行 ” 這句話也沒有得到驗證,222照常打印出來,又是什麼原因?

原因請注意上述代碼,result函數並不是異步函數,僅僅只是個帶有延遲的函數,如果我更改一個result函數,加上Promise,使它成為一個異步函數,我們再來看一下:

// 此刻我們寫一個異步方法
function result() {
    return new Promise((resolved,reject)=>{
        setTimeout(() => {
            resolved(console.log('異步請求'))
        }, 2000);
    })
}

// 然後使用這個方法,需要用到await
async function foo(){
    console.log('111')
    await result();
    console.log('222');
  }
foo()
console.log('333')

打印結果如下:

image

先打印出111和333,2秒鐘過後,打印出“異步請求”和222,所以這也是證明了 async/await 是操作異步方法,在返回結果之前,await後的代碼,是不執行的。

async/await可以捕獲到錯誤信息,利用try...catch,只需要在要捕獲的地方加上這個方法即可:

function result() {
    return new Promise((resolved,reject)=>{
        setTimeout(() => {
            reject(console.log('異步請求'))
        }, 2000);
    })
}

// 然後使用這個方法,需要用到await
async function foo(){
    console.log('111')
    try{
        await result();
        console.log('222');
    }catch(err){
        console.log(err)
    }
  }
foo()
console.log('333')

image

上邊的undefined就是錯誤信息,只不過這個案例沒有錯誤信息;在實際項目中,有一些錯誤,我們就可以用這種方式捕獲到錯誤信息,(try...catch語句用於處理代碼中可能出現的錯誤信息),如果用在Promise當中,捕獲錯誤信息就會很麻煩。這也是async的一個優點。

這樣看起來 async/await 的寫法,很像同步的寫法,但是卻可以像Promise一樣去處理異步,這就是一種代碼簡潔的好處,如果有多個異步需要處理,不再需要像Promise那樣很多 .then 代碼橫向發展,我們來做一下對比,例如有ABCD四個異步需要觸發:
Promise寫法

const result = () => {
  return aaa()
    .then(() => A())
    .then(() => B())
    .then(() => C())
    .then(() => D())
}

result()
  .catch(err => {
    console.log(err);
  })

async/await寫法

const result = async () => {
  await A()
  await B()
  await C()
  await D()
}

result()
  .catch(err => {
    console.log(err);
  })

上述的代碼,只是為了證明async/await寫法的可讀性會更好,我們在寫項目時,在返回的結果中會做很多處理,這種寫法在多層嵌套中,你就會發覺它的優勢了。

以上就是簡單介紹關於async/await的用法,還有很多的用法,例如取中間值,All方法等等,同學們可以進一步去查閱資料,在實際項目中,掌握以上的關鍵的知識點,是必要的。

有興趣的同學也可以查詢async/await的原理,包括進一步學習關於generator函數,並且可以在評論區留言,共同進步。

weChat:VillinWeChat

歡迎提出寶貴意見

Leave a Reply

Your email address will not be published. Required fields are marked *