async/await

上次我們大概說了一下 Promise 晋被,今天就接著講 async/await 這組 API 。

async/await 是 ES7 的標(biāo)準(zhǔn)刚盈,Promise 是 ES6 標(biāo)準(zhǔn)羡洛,async/await 這套 API 也是用來幫助我們寫異步代碼的,它是構(gòu)建在 Promise 之上的藕漱,有點(diǎn)像 Okhttp 和 Retrofit 的關(guān)系欲侮。

什么是 async ?

async function myFirstAsyncFunction() {
  try {
    const fulfilledValue = await doSomeThing();
  }
  catch (rejectedValue) {
    // …
  }
}

大概長上面這個樣子谴分。async 一般不單獨(dú)使用锈麸,而是和 await 一起使用,一個 async 函數(shù)內(nèi)部可能有 零個 或者 多個 await 牺蹄。

這段代碼即使沒有學(xué)過 Promise 也很容易看懂忘伞。這就是 async 函數(shù)的優(yōu)勢所在。

async 函數(shù)被調(diào)用的時候,會立即返回一個 Promise氓奈。

await

await 不能單獨(dú)使用翘魄,如果在非 async 函數(shù)內(nèi)部被調(diào)用會報錯。await 后面一般跟一個 Promise 舀奶,也可以是其他的暑竟,比如一個數(shù)值,或者一個變量育勺,或者一個函數(shù)但荤。如果 await 后面不是一個 Promise 就會返回一個已經(jīng) resolve 的 Promise。

當(dāng) async 函數(shù)執(zhí)行到 await 的時候涧至,會暫停整個async函數(shù)的執(zhí)行進(jìn)程并出讓其控制權(quán)腹躁,只有當(dāng)其等待的基于Promise 的異步操作被兌現(xiàn)或被拒絕之后才會恢復(fù)進(jìn)程。

當(dāng)然南蓬,async 函數(shù)也會返回一個 Promise 纺非,也就是說,await 后面也可以跟一個 async 函數(shù)赘方。

async/await 這套 API 烧颖,感覺有 Kotlin 中的 協(xié)程 和 掛起函數(shù) 內(nèi)味。

為什么要使用 async 窄陡?

隱藏 Promise 炕淮,更易于理解

假設(shè)我們想請求一個接口,然后把響應(yīng)的數(shù)據(jù)打印出來泳梆,并且捕獲異常鳖悠。用 Promise 大概是這樣寫:

function logFetch(url) {
  return fetch(url)
    .then(response => response.text())
    .then(text => {
      console.log(text);
    }).catch(err => {
      console.error('fetch failed', err);
    });
}

如果用 async 函數(shù)來寫,大概是這個樣子:

async function logFetch(url) {
  try {
    const response = await fetch(url);
    console.log(await response.text());
  }
  catch (err) {
    console.log('fetch failed', err);
  }
}

雖然代碼的行數(shù)差不多优妙,但是代碼看起來更加簡潔乘综,少了很多 then 的嵌套。請求一個接口數(shù)據(jù)套硼,然后打印卡辰,就像你看到的,很簡單邪意。

就像剛才的那個例子九妈,如果沒學(xué)過 Promise 也不影響你看懂和理解 async 代碼。這個好像沒什么雾鬼,但是對于一個大型的項目萌朱,人員的技術(shù)水平參差不齊,不能保證所有人都熟悉和理解 Promise 策菜,我們要做的是盡可能的降低代碼的理解難度晶疼,使大部分人都能看懂酒贬。

用同步的思路寫異步邏輯

async/await 最大的優(yōu)勢就是我們可以用同步的思路來寫異步的業(yè)務(wù)邏輯,所以代碼整體看起來更加容易看懂翠霍。我們舉個例子锭吨。

上面的代碼還是比較簡單,我們再舉一個復(fù)雜一點(diǎn)的例子寒匙。

我們想獲取一個網(wǎng)絡(luò)資源的大小零如,如果使用 Promise 大概可能是這個樣子:

function getResponseSize(url) {
  return fetch(url).then(response => {
    const reader = response.body.getReader();
    let total = 0;

    return reader.read().then(function processResult(result) {
      if (result.done) return total;

      const value = result.value;
      total += value.length;
      console.log('Received chunk', value);

      return reader.read().then(processResult);
    })
  });
}

即使你學(xué)過 Promise ,這個代碼也并不是很好理解锄弱,更別說是沒有學(xué)過 Promise 的同學(xué)了考蕾。因為中間有一個循環(huán)的過程,而且這個執(zhí)行的過程的異步的会宪,并不想我們之前學(xué)到的一個鏈?zhǔn)秸{(diào)用能解決的辕翰。當(dāng)然,你也可以抽取一下狈谊,使代碼更簡潔一點(diǎn)。

const processResult = (result) =>{
      if (result.done) return total;

      const value = result.value;
      total += value.length;
      console.log('Received chunk', value);

      return reader.read().then(processResult);
}

function getResponseSize(url) {
  return fetch(url).then(response => {
    const reader = response.body.getReader();
    let total = 0;

    return reader.read().then(processResult)
  });
}

但是多了一個方法沟沙,勉勉強(qiáng)強(qiáng)吧河劝。但是我們看一下 async 函數(shù)是怎么處理的。

async function getResponseSize(url) {
  const response = await fetch(url);
  const reader = response.body.getReader();
  let result = await reader.read();
  let total = 0;

  while (!result.done) {
    const value = result.value;
    total += value.length;
    console.log('Received chunk', value);
    // get the next result
    result = await reader.read();
  }

  return total;
}

OHHHHHHHHH

這個是不是看起來就更加流暢了矛紫?因為 await 表達(dá)式會阻塞運(yùn)行赎瞎,甚至可以直接阻塞循環(huán),所以整體看起來像同步的代碼颊咬,也更符合直覺务甥,更容易讀懂這個代碼。

小心 await 阻塞

由于 await 能夠阻塞 async 函數(shù)的運(yùn)行喳篇,所以代碼看起來更像同步的代碼敞临,更容易閱讀和理解。但是要小心 await 阻塞麸澜,因為有些阻塞是不必要的挺尿,不恰當(dāng)使用可能會影響代碼的性能。

假如我們要把一個網(wǎng)絡(luò)數(shù)據(jù)和本地數(shù)據(jù)合并炊邦,錯誤的實例可能是這樣子:

async function combineData(url, file) {
    let networkData = await fetch(url)
    let fileData = await readeFile(file)
    console.log(networkData + fileData)
}

其實我們不用等一個文件讀完了编矾,再去讀下個文件,我們可以兩個文件一起讀馁害,讀完之后再進(jìn)行合并窄俏,這樣能提高代碼的運(yùn)行速度。我們可以這樣寫:

async function combineData(url, file) {
    let fetchPromise = fetch(url)
    let readFilePromise = readFile(file)
    let networkData = await fetchPromise
    let fileData = await readFilePromise
    console.log(networkData + fileData)
}

這樣的話碘菜,就可以同時 網(wǎng)絡(luò)請求 和 讀取文件 了凹蜈,可以節(jié)省很多時間限寞。這里主要是利用了 Promise 一旦創(chuàng)建就立刻執(zhí)行的特點(diǎn),不懂的同學(xué)可以復(fù)習(xí)一下上一節(jié)的內(nèi)容踪区。

當(dāng)然昆烁,如果你熟悉 Promise 的話,可以直接使用 Promise.all 的方式來處理缎岗,或者 await 后面跟 Promise.all 這里就不展開講了静尼。

異常處理

try...catch

在 async 函數(shù)中,異常處理一般是 try...catch 传泊,如果沒有進(jìn)行 try...catch 鼠渺,await 表達(dá)式一旦 reject ,async 函數(shù)返回的 Promise 就會 reject 眷细。

其實結(jié)合 Promise 來看拦盹,如果一個 Promise 狀態(tài)敲定為 reject ,并且后續(xù)的 then 沒有傳入 reject 函數(shù)溪椎,或者沒有 catch 普舆,那么就會拋出異常。從這個角度來看校读,在 async 函數(shù)中用 try...catch 來包住 await 表達(dá)式沼侣,可能就是 catch 住這個異常,并且把這個 reject 信息傳到 catch 里面歉秫。

這里就不舉例子了蛾洛。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市雁芙,隨后出現(xiàn)的幾起案子轧膘,更是在濱河造成了極大的恐慌,老刑警劉巖兔甘,帶你破解...
    沈念sama閱讀 210,978評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件谎碍,死亡現(xiàn)場離奇詭異,居然都是意外死亡洞焙,警方通過查閱死者的電腦和手機(jī)椿浓,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,954評論 2 384
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來闽晦,“玉大人扳碍,你說我怎么就攤上這事∠沈龋” “怎么了笋敞?”我有些...
    開封第一講書人閱讀 156,623評論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長荠瘪。 經(jīng)常有香客問我夯巷,道長赛惩,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,324評論 1 282
  • 正文 為了忘掉前任趁餐,我火速辦了婚禮喷兼,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘后雷。我一直安慰自己季惯,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,390評論 5 384
  • 文/花漫 我一把揭開白布臀突。 她就那樣靜靜地躺著勉抓,像睡著了一般。 火紅的嫁衣襯著肌膚如雪候学。 梳的紋絲不亂的頭發(fā)上藕筋,一...
    開封第一講書人閱讀 49,741評論 1 289
  • 那天,我揣著相機(jī)與錄音梳码,去河邊找鬼隐圾。 笑死,一個胖子當(dāng)著我的面吹牛掰茶,可吹牛的內(nèi)容都是我干的翎承。 我是一名探鬼主播,決...
    沈念sama閱讀 38,892評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼符匾,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了瘩例?” 一聲冷哼從身側(cè)響起啊胶,我...
    開封第一講書人閱讀 37,655評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎垛贤,沒想到半個月后焰坪,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,104評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡聘惦,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評論 2 325
  • 正文 我和宋清朗相戀三年某饰,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,569評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖离陶,靈堂內(nèi)的尸體忽然破棺而出控乾,到底是詐尸還是另有隱情,我是刑警寧澤洼哎,帶...
    沈念sama閱讀 34,254評論 4 328
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響减途,放射性物質(zhì)發(fā)生泄漏酣藻。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,834評論 3 312
  • 文/蒙蒙 一鳍置、第九天 我趴在偏房一處隱蔽的房頂上張望辽剧。 院中可真熱鬧,春花似錦税产、人聲如沸怕轿。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,725評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽撤卢。三九已至,卻和暖如春梧兼,著一層夾襖步出監(jiān)牢的瞬間放吩,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,950評論 1 264
  • 我被黑心中介騙來泰國打工羽杰, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留渡紫,地道東北人。 一個月前我還...
    沈念sama閱讀 46,260評論 2 360
  • 正文 我出身青樓考赛,卻偏偏與公主長得像惕澎,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子颜骤,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,446評論 2 348

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