[譯]JavaScript Async/Await 秒殺 Promise 的6個理由(教學(xué)向)

原著:6 Reasons Why JavaScript's Async/Await Blows Promises Away (Tutorial)

作者:Mostafa Gaafar

譯者:Tingrui Li

先說一件事,Node自從7.6版本之后已經(jīng)原生支持async/await語法域蜗。如果你還未嘗試過它巨双,這里有一些例子告訴你為何你應(yīng)當馬上開始使用它,并不再回頭霉祸。

Async/await 101

對于那些沒有聽說過這個玩意兒的人筑累,這里有一些快速掃盲:

  • Async/await是編寫異步代碼新的方式,之前我們使用回調(diào)函數(shù)和Promise丝蹭。
  • Async/await實際上是基于Promise的慢宗。它不能與常規(guī)回調(diào)函數(shù)和node回調(diào)函數(shù)一起使用。
  • Async/await跟Promise一樣奔穿,不會阻塞程序婆廊。
  • Async/await讓異步代碼看起來更像同步執(zhí)行的。這就是它強大的地方所在巫橄。

語法

假設(shè)一個方法 getJSON 返回一個Promise淘邻,這個Promise解析(resolve)出一個JSON對象,我們想要調(diào)用他并且打印那個JSON湘换,返回"done"宾舅。

你用Promise通常會這樣實現(xiàn):

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

makeRequest()

而如果你用async/await:

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

makeRequest()

這里有一些區(qū)別:

  1. 我們的方法前面有一個關(guān)鍵字async。而await關(guān)鍵字只能被用于以async定義的方法內(nèi)部彩倚。任何async方法都隱式地返回一個Promise筹我,并且解析(resolve)的值是你從方法內(nèi)return出來的東西(在這個例子中就是string "done")。

  2. 上面第一點表示我們不能在外層是用await帆离,因為我們只能在async方法中使用它蔬蕊。

// 在外層不好使
// await makeRequest()

// 好使
makeRequest().then((result) => {
  // do something
})
  1. await getJSON()表示console.log會等到getJSON()這個Promise resolve之后才會打印它的值。

為什么這樣更好哥谷?

1. 簡潔岸夯、干凈

瞧瞧我們少寫了多少東西!就算是上面這么一個簡短的例子们妥,我們都明顯少寫了很多代碼猜扮。不需要寫.then,創(chuàng)建異步函數(shù)去處理response监婶,或者對我們根本用不到的變量起data這個名字旅赢。我們也避免了把代碼寫成很多層齿桃。在下面的例子中,將會凸顯這一點煮盼。

2. 錯誤處理

Async/await讓我們終于可以以同一種方法同時處理同步和異步錯誤:經(jīng)典的try/catch短纵。在下面這個Promise的例子中,try/catch不會處理JSON.parse錯誤僵控,因為那是在Promise中發(fā)生的香到。我們得在Promise中去.catch,然后重新寫一次我們的錯誤處理代碼喉祭,這可能比你的生產(chǎn)環(huán)境代碼中的那些console.log要冗余的多。

const makeRequest = () => {
  try {
    getJSON()
      .then(result => {
        // JSON.parse可能會失敗
        const data = JSON.parse(result)
        console.log(data)
      })
      // 處理JSON.parse可能拋出的錯誤
      .catch((err) => {
        console.log(err)
      })
  } catch (err) {
    console.log(err)
  }
}

現(xiàn)在我們用async/await寫這段代碼雷绢。catch代碼塊現(xiàn)在會處理JSON轉(zhuǎn)換錯誤了泛烙。

const makeRequest = async () => {
  try {
    // JSON.parse可能會失敗
    const data = JSON.parse(await getJSON())
    console.log(data)
  } catch (err) {
    console.log(err)
  }
}

是不是好棒棒?

3. 條件判斷

想象一下這個場景翘紊,一段代碼需要獲取一些數(shù)據(jù)然后決定是返回這些數(shù)據(jù)蔽氨,還是根據(jù)這些數(shù)據(jù)中的一些值來獲取更多的數(shù)據(jù)。

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

是不是看著就頭大帆疟?這六層的代碼很容易讓你頭暈?zāi)垦p木俊D切├ㄌ枺约皉eturn語句只是為了將最終結(jié)果傳遞到主要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. 中間值

你可能遇到這種情況:你需要調(diào)用promise1然后用它的返回值去調(diào)用promise2,然后用兩者的返回值去調(diào)用promise3柳琢,你的代碼很可能是這樣的:

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

如果promise3不需要value1绍妨,這個層級關(guān)系會清楚一些。如果你不能忍受柬脸,你可以用promise.all包裝value1和value2:

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

這種寫法犧牲了可讀性他去,value1value2不應(yīng)當出于任何理由屬于同一個數(shù)組。

同樣的邏輯如果使用async/await來寫會變得很簡單倒堕,會讓你懷疑以前為什么掙扎著讓使用Promise的代碼看起來更簡單:

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

5. 錯誤棧

想像這樣一個情景:連續(xù)鏈式調(diào)用多個Promise灾测,然后其中某個地方可能會拋出異常:

const makeRequest = () => {
  return callAPromise()
    .then(() => callAPromise())
    .then(() => callAPromise())
    .then(() => callAPromise())
    .then(() => callAPromise())
    .then(() => {
      throw new Error("oops");
    })
}

makeRequest()
  .catch(err => {
    console.log(err);
    // 輸出
    // Error: oops at callAPromise.then.then.then.then.then (index.js:8:13)
  })

這個錯誤輸出棧不能明確的指示錯誤發(fā)生在哪里。甚至?xí)a(chǎn)生誤導(dǎo):整個錯誤只包含一個方法名then垦巴。

然而媳搪,async/await的錯誤棧會指向具體產(chǎn)生錯誤的方法:

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

其實當你本地開發(fā)調(diào)試時,這一點意義不大骤宣。但是如果你需要在生產(chǎn)環(huán)境服務(wù)器上的代碼上找錯時蛾号,就非常實用了。在這種情況下涯雅,知道錯誤發(fā)生在makeRequest比知道錯誤發(fā)生在then.then.then.then...要好很多......

6. 調(diào)試

最后鲜结,async/await一個巨大的優(yōu)勢是它非常容易調(diào)試。對Promise進行調(diào)試出于兩個原因非常的痛苦:

  1. 你不能在返回表達式的箭頭函數(shù)上設(shè)置斷點(沒有函數(shù)體)。
const makeRequest = () =>{
reutrn callAPromise()
.then(()=>callAPromise())
.then(()=>callAPromise())
.then(()=>callAPromise())
.then(()=>callAPromise())
}
  1. 如果你在一個.then塊內(nèi)設(shè)置了斷點精刷,然后用step-over等功能時拗胜,調(diào)試器不會進入.then代碼因為他只會"step"進同步代碼。

使用async/await你不需要那么多箭頭函數(shù)怒允,你可以直接"step"那些await調(diào)用埂软,就像普通的同步調(diào)用一樣。

const makeRequest = async () =>{
  await callAPromise()
  await callAPromise()
  await callAPromise()
  await callAPromise()
  await callAPromise()
}

結(jié)論

Async/await是Javascript近幾年來最具有革命性的特性之一纫事,它讓你體會到Promise是多么的混亂勘畔,然后給你一個方便的替代品。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末丽惶,一起剝皮案震驚了整個濱河市炫七,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌钾唬,老刑警劉巖万哪,帶你破解...
    沈念sama閱讀 218,284評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異抡秆,居然都是意外死亡奕巍,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,115評論 3 395
  • 文/潘曉璐 我一進店門儒士,熙熙樓的掌柜王于貴愁眉苦臉地迎上來的止,“玉大人,你說我怎么就攤上這事着撩〕迳保” “怎么了?”我有些...
    開封第一講書人閱讀 164,614評論 0 354
  • 文/不壞的土叔 我叫張陵睹酌,是天一觀的道長权谁。 經(jīng)常有香客問我,道長憋沿,這世上最難降的妖魔是什么旺芽? 我笑而不...
    開封第一講書人閱讀 58,671評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮辐啄,結(jié)果婚禮上采章,老公的妹妹穿的比我還像新娘。我一直安慰自己壶辜,他們只是感情好悯舟,可當我...
    茶點故事閱讀 67,699評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著砸民,像睡著了一般抵怎。 火紅的嫁衣襯著肌膚如雪奋救。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,562評論 1 305
  • 那天反惕,我揣著相機與錄音尝艘,去河邊找鬼。 笑死姿染,一個胖子當著我的面吹牛背亥,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播悬赏,決...
    沈念sama閱讀 40,309評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼狡汉,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了闽颇?” 一聲冷哼從身側(cè)響起盾戴,我...
    開封第一講書人閱讀 39,223評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎进萄,沒想到半個月后捻脖,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體锐峭,經(jīng)...
    沈念sama閱讀 45,668評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡中鼠,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,859評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了沿癞。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片援雇。...
    茶點故事閱讀 39,981評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖椎扬,靈堂內(nèi)的尸體忽然破棺而出惫搏,到底是詐尸還是另有隱情,我是刑警寧澤蚕涤,帶...
    沈念sama閱讀 35,705評論 5 347
  • 正文 年R本政府宣布筐赔,位于F島的核電站,受9級特大地震影響揖铜,放射性物質(zhì)發(fā)生泄漏茴丰。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,310評論 3 330
  • 文/蒙蒙 一天吓、第九天 我趴在偏房一處隱蔽的房頂上張望贿肩。 院中可真熱鬧,春花似錦龄寞、人聲如沸张惹。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,904評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春封寞,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,023評論 1 270
  • 我被黑心中介騙來泰國打工鹦蠕, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人在抛。 一個月前我還...
    沈念sama閱讀 48,146評論 3 370
  • 正文 我出身青樓钟病,卻偏偏與公主長得像,于是被迫代替她去往敵國和親刚梭。 傳聞我的和親對象是個殘疾皇子肠阱,可洞房花燭夜當晚...
    茶點故事閱讀 44,933評論 2 355

推薦閱讀更多精彩內(nèi)容