首頁  >  文章  >  web前端  >  js非同步回呼Async/Await與Promise的區別,Async/Await取代Promise的6個理由

js非同步回呼Async/Await與Promise的區別,Async/Await取代Promise的6個理由

青灯夜游
青灯夜游原創
2018-09-12 16:45:321587瀏覽

本章跟大家介紹js非同步回調Async/Await與Promise的差別,Async/Await取代Promise的6個理由。有一定的參考價值,有需要的朋友可以參考一下,希望對你有幫助。

什麼是Async/Await?

#async/await是寫非同步程式碼的新方式,先前的方法有回呼函數和Promise。

async/await是基於Promise實現的,它不能用於普通的回呼函數。

async/await與Promise一樣,是非阻塞的。

async/await使得非同步程式碼看起來像同步程式碼,這正是它的魔力所在。

Async/Await語法

範例中,getJSON函數回傳一個promise,這個promise成功resolve時會回傳一個JSON物件。我們只是呼叫這個函數,印出返回的JSON對象,然後返回"done"。

使用Promise是這樣的:

const makeRequest = () =>
  getJSON()
    .then(data => {
      console.log(data)
      return "done"
    })makeRequest()

使用Async/Await是這樣的:

const makeRequest = async () => {
  console.log(await getJSON())
  return "done"}makeRequest()

它們有一些細微不同:

函數前面多了一個aync關鍵字。 await關鍵字只能用在aync定義的函數內。 async函數會隱式地回傳一個promise,該promise的reosolve值就是函數return的值。 (範例中reosolve值就是字串"done")

第1點暗示我們不能在最外層程式碼中使用await,因為不在async函數內。

// 不能在最外层代码中使用
awaitawait makeRequest()
// 这是会出事情的 
makeRequest().then((result) => {
  // 代码
 })

await getJSON()表示console.log會等到getJSON的promise成功reosolve之後再執行。

為什麼Async/Await比較好?

1. 簡潔

由範例可知,使用Async/Await明顯節約了不少程式碼。我們不需要寫.then,不需要寫匿名函數處理Promise的resolve值,也不需要定義多餘的data變量,還避免了嵌套程式碼。這些小的優點會迅速累積起來,這在之後的程式碼範例中會更加明顯。

2. 錯誤處理

Async/Await讓try/catch可以同時處理同步和非同步錯誤。在下面的promise範例中,try/catch不能處理JSON.parse的錯誤,因為它在Promise中。我們需要使用.catch,這樣錯誤處理程式碼非常冗餘。並且,在我們的實際生產程式碼會更加複雜。

const makeRequest = () => {
  try {
    getJSON()
      .then(result => {
        // JSON.parse可能会出错
        const data = JSON.parse(result)
        console.log(data)
      })
      // 取消注释,处理异步代码的错误
      // .catch((err) => {
      //   console.log(err)
      // })
  } catch (err) {
    console.log(err)
  }}

使用aync/await的話,catch能處理JSON.parse錯誤:

const makeRequest = async () => {
  try {
    // this parse may fail
    const data = JSON.parse(await getJSON())
    console.log(data)
  } catch (err) {
    console.log(err)
  }}

3. 條件語句

下面範例中,需要取得數據,然後根據傳回數據決定是直接返回,還是繼續取得更多的數據。 ·

const makeRequest = () => {
  return getJSON()
    .then(data => {
      if (data.needsAnotherRequest) {
        return makeAnotherRequest(data)
          .then(moreData => {
            console.log(moreData)
            return moreData          })
      } else {
        console.log(data)
        return data      }
    })}

這些程式碼看著就頭痛。嵌套(6層),括號,return語句很容易讓人感到迷茫,而它們只是需要將最終結果傳遞到最外層的Promise。

上面的程式碼使用async/await編寫可以大大地提高可讀性:

const makeRequest = async () => {
  const data = await getJSON()
  if (data.needsAnotherRequest) {
    const moreData = await makeAnotherRequest(data);
    console.log(moreData)
    return moreData  } else {
    console.log(data)
    return data    
  }}

4. 中間值

你很可能遇到過這樣的場景,調用promise1,使用promise1回傳的結果去呼叫promise2,然後使用兩者的結果去呼叫promise3。你的程式碼很可能是這樣的:

const makeRequest = () => {
  return promise1()
    .then(value1 => {
      return promise2(value1)
        .then(value2 => {        
          return promise3(value1, value2)
        })
    })}

如果promise3不需要value1,可以很簡單地將promise巢狀鋪平。如果你忍受不了嵌套,你可以將value 1 & 2 放進Promise.all來避免深層嵌套:

const makeRequest = () => {
  return promise1()
    .then(value1 => {
      return Promise.all([value1, promise2(value1)])
    })
    .then(([value1, value2]) => {      
      return promise3(value1, value2)
    })}

這種方法為了可讀性犧牲了語意。除了避免嵌套,並沒有其他理由將value1和value2放在一個陣列中。

使用async/await的話,程式碼會變得異常簡單和直覺。

const makeRequest = async () => {
  const value1 = await promise1()
  const value2 = await promise2(value1)
  return promise3(value1, value2)}

5. 錯誤堆疊

下面範例中呼叫了多個Promise,假設Promise鏈中某個地方拋出了一個錯誤:

const makeRequest = () => {
  return callAPromise()
    .then(() => callAPromise())
    .then(() => callAPromise())
    .then(() => callAPromise())
    .then(() => callAPromise())
    .then(() => {
      throw new Error("oops");
    })}makeRequest()
  .catch(err => {
    console.log(err);
    // output
    // Error: oops at callAPromise.then.then.then.then.then (index.js:8:13)
  })

Promise鏈中返回的錯誤棧沒有給出錯誤發生位置的線索。更糟的是,它會誤導我們;錯誤堆疊中唯一的函數名為callAPromise,然而它和錯誤沒有關係。 (檔名和行號還是有用的)。

然而,async/await中的錯誤堆疊會指向錯誤所在的函數:

const makeRequest = async () => {
  await callAPromise()
  await callAPromise()
  await callAPromise()
  await callAPromise()
  await callAPromise()
  throw new Error("oops");}makeRequest()
  .catch(err => {
    console.log(err);
    // output
    // Error: oops at makeRequest (index.js:7:9)
  })

在開發環境中,這一點優勢並不大。但是,當你分析生產環境的錯誤日誌時,它將非常有用。這時,知道錯誤發生在makeRequest比知道錯誤發生在then鏈中要好。

6. 偵錯

最後一點,也是非常重要的一點在於,async/await能夠讓程式碼偵錯更簡單。 2個理由使得偵錯Promise變得非常痛苦:

1)不能在返回表達式的箭頭函數中設定斷點 

js非同步回呼Async/Await與Promise的區別,Async/Await取代Promise的6個理由

2)如果你在.then程式碼區塊中設定斷點,使用Step Over快捷鍵,偵錯器不會跳到下一個.then,因為它只會跳過非同步程式碼。

使用await/async時,你不再需要那麼多箭頭函數,這樣你就可以像調試同步程式碼一樣跳過await語句

js非同步回呼Async/Await與Promise的區別,Async/Await取代Promise的6個理由

結論

Async/Await是近年來JavaScript添加的最革命性的特性之一。它會讓你發現Promise的語法有多糟糕,而且提供了一個直覺的替代方法。

憂慮

#

對於Async/Await,也許你有一些合理的懷疑:
   它使得非同步程式碼不在明顯: 我們已經習慣了用回調函數或.then來識別非同步程式碼,我們可能需要花數個星期去習慣新的標誌。但是,C#擁有這個特性已經很多年了,熟悉它的朋友應該知道暫時的稍微不方便是值得的。
   Node 7不是LTS(長期支援版本): 但是,Node 8下個月就會發布,將程式碼遷移到新版本會非常簡單。

以上是js非同步回呼Async/Await與Promise的區別,Async/Await取代Promise的6個理由的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn