細(xì)讀 ES6 | async/await

配圖源自 Freepik

前一篇文章中,最后提到 Generator 的應(yīng)用,很實(shí)際場景可能需要自實(shí)現(xiàn)一個(gè) Generator 函數(shù)執(zhí)行器。因此,用起來還是很麻煩的。

現(xiàn)在有另外一個(gè)替代方法,那就是 ES7 標(biāo)準(zhǔn)中引入的 async 函數(shù),它使得異步操作變得更加方便。

Async 函數(shù),其實(shí)就是 Generator 函數(shù)的語法糖。

一、語法

定義一個(gè) Async 函數(shù)語法非常簡單,在函數(shù)名稱之前加上 async 關(guān)鍵字即可。

async function foo {
  // 內(nèi)部的 await 語句是可選的
}

// ?? Async 函數(shù)注意點(diǎn):
// 1. 函數(shù)體內(nèi)部的 await 語句是可選的;
// 2. 當(dāng)內(nèi)部含有 await 語句時(shí),表示有異步操作;
// 3. 針對(duì)類似 let a = await 1 的語句,語法上是允許的,但這里使用 await 是無意義的;
// 4. 針對(duì)同步代碼,只要類似 let a = 1 這樣寫即可,無需使用 await 關(guān)鍵字。

也可以按以下方式去定義:

// 函數(shù)聲明形式
async function foo {}

// 函數(shù)表達(dá)式形式(匿名或具名均可)
const bar = async function () {}

// 箭頭函數(shù)形式
const baz = async () => {}

// Class 的方法
class Foo {
  async sayHi() {}
  getName = async () => {}
  // 語法沒問題,但兩者的區(qū)別是:
  // sayHi 方法掛載到 Foo 原型上,而 getName 方法則是掛載到實(shí)例對(duì)象上
}

Async 函數(shù)總返回給一個(gè) Promise 對(duì)象。

因此,調(diào)用方式也很忒簡單。相比 Generator 函數(shù),簡直不要太爽了。

foo()
  .then(res => { /* res 為 foo 函數(shù)的 return 值 */ })
  .catch(err => { /* err 為 foo 函數(shù)的異常原因 */ })

async 函數(shù)內(nèi)部 return 語句返回的值,會(huì)成為 then() 方法回調(diào)函數(shù)的參數(shù),即 Promise 對(duì)象的狀態(tài)變?yōu)?fulfilled。若無顯式的 return 語句,相當(dāng)于 return undefined,自然 then() 方法接收到的參數(shù)也就是 undefined。

若內(nèi)部拋出錯(cuò)誤,會(huì)導(dǎo)致返回的 Promise 對(duì)象變?yōu)?rejected 狀態(tài),從而被 Promise 對(duì)象的 catch() 方法捕獲到。

二、特點(diǎn)

Async 函數(shù)是 Generator 函數(shù)的語法糖,它對(duì) Generator 函數(shù)的改進(jìn),體現(xiàn)在以下四點(diǎn):

  • 內(nèi)置執(zhí)行器

我們都知道,Generator 函數(shù)的執(zhí)行必須依賴執(zhí)行器,執(zhí)行器內(nèi)部就是不斷執(zhí)行生成器的 next() 方法的過程。所以才有了 co 模塊。而 async 函數(shù)則內(nèi)置了執(zhí)行器。調(diào)用方法也只需要跟普通函數(shù)那樣,一行就行。

// Async 函數(shù)
async function foo() { /* do something... */ }
foo()

// Generator 函數(shù)
function* bar() { /* do something... */ }
const gen = bar()
gen.next() // 這才開始執(zhí)行 Generator 函數(shù)體內(nèi)的代碼
// ...
  • 更好的語義

asyncawait,比起星號(hào) *yield,語義更清晰。async 表示函數(shù)里有異步操作,await 表示緊跟在后面的表達(dá)式需要等待結(jié)果。

// Async 函數(shù)
async function foo() {
  let a = await 1
  let b = await 2
  return b
  // 直接調(diào)用 foo() 且 a、b 的值對(duì)應(yīng)為 1、2
}
foo() // Promise { <fulfilled>: 2 }

// Generator 函數(shù)
function* bar() {
  let a = yield 1
  let b = yield 2
  return b
  // 若按如下方式調(diào)用,a、b 的值均為 undefined,而非對(duì)應(yīng)為 1、2
}
const gen = bar()
gen.next() // { done: false, value: 1 }
gen.next() // { done: false, value: 2 }
gen.next() // { done: true, value: undefined }
  • 更廣的適用性

co 模塊約定,yield 命令后面只能是 Thunk 函數(shù)或 Promise 對(duì)象,而 async 函數(shù)的 await 命令后面,可以是 Promise 對(duì)象或非 Promise 的值(原始值或引用值均可,但這時(shí)內(nèi)部會(huì)自動(dòng)使用 Promise.resolve() 轉(zhuǎn)換為 Promise 對(duì)象)。

  • 返回值是 Promise 對(duì)象

async 函數(shù)的返回值總是 Promise 對(duì)象,這比 Generator 函數(shù)的返回值是 Iterator(迭代器)對(duì)象方便多了。你可以用 Promise.prototype.then() 方法指定下一步的操作。

三、基本用法

async 函數(shù)返回的 Promise 對(duì)象,必須等到內(nèi)部所有await 命令后面的 Promise 對(duì)象執(zhí)行完(即狀態(tài)變?yōu)?fullfilled),且內(nèi)部不發(fā)生錯(cuò)誤的情況下,最終 async 函數(shù)返回的 Promise 對(duì)象才會(huì)變成 fulfilled 狀態(tài),從而觸發(fā) Promise 對(duì)象的 then() 方法的回調(diào)函數(shù)。

若函數(shù)內(nèi)部 await 后面的某個(gè) Promise 對(duì)象變?yōu)?rejected 狀態(tài)(且函數(shù)內(nèi)未使用 try...catch 捕獲處理),或者函數(shù)內(nèi)部出現(xiàn)拋出錯(cuò)誤,將會(huì)停止執(zhí)行函數(shù)體內(nèi)后面的代碼,并使得最終 async 函數(shù)返回的 Promise 對(duì)象會(huì)變成 rejected 狀態(tài),從而觸發(fā) catch() 的回調(diào)函數(shù)。

看著有點(diǎn)像 Promise.all() 方法,但兩者是有區(qū)別的,這里先不展開,下文再詳說。

看一個(gè)簡單的例子:

async function request() {
  const response = await fetch('/user/1')
  const res = await response.json()
  return res
}

// 如果使用 Promise 是這樣的,無論在語義和寫法上都不如 async 函數(shù)清晰、簡潔。
// 而且這例子只有一個(gè)異步操作,若存在多個(gè),那 then 方法真的不忍直視。
// function request() {
//   return fetch('/user/1')
//     .then(response => response.json())
//     .then(res => res)
// }

然后按照下面那樣去調(diào)用即可。

request()
  .then(res => {
    console.log(res)
    // do something...
  })
  .catch(err => {
    console.warn(err)
    // 處理異常
    // 例如網(wǎng)絡(luò)問題,導(dǎo)致 fetch 請求出錯(cuò)了,自然 response (它是一個(gè) Promise 對(duì)象)的結(jié)果
    // 就不是一個(gè) JSON 數(shù)據(jù),那么調(diào)用 response.json() 解析就會(huì)出錯(cuò)。(這時(shí)可使用 response.text() 進(jìn)行解析)
    // 使得 request 函數(shù)停止往下執(zhí)行,且返回的 Promise 狀態(tài)變?yōu)?rejected,
    // 自然就會(huì)被這里的 catch 捕獲到。錯(cuò)誤信息可能是:SyntaxError: Unexpected token < in JSON at position 0
  })
1. await 命令

其中 await 關(guān)鍵字,目前只能在 async 函數(shù)內(nèi)部使用。

但未來就不一定了。現(xiàn)在有一個(gè)語法提案,允許在模塊頂層獨(dú)立使用 await 命令,目前以進(jìn)入 Stage-4 階段,相信在不久的將來就能正式納入 ECMAScript 標(biāo)準(zhǔn)了。關(guān)于它的用法可看:Top-level await。

正常情況下,await 關(guān)鍵字后面應(yīng)該接著一個(gè) Promise 對(duì)象,并返回該對(duì)象的結(jié)果。如果不是 Promise 對(duì)象,就直接返回對(duì)應(yīng)的值。

async function foo() {
  return await 1
  // 相當(dāng)于 return 1
  // 
  // 對(duì)于 `await 1` 函數(shù)內(nèi)部的執(zhí)行器,其實(shí)做的事情是 Promise.resolve(1),
  // 而 Promise.resolve() 的作用就是將某個(gè)值轉(zhuǎn)化為 Promise 對(duì)象。
  // 關(guān)于 Promise.resolve() 可看文章:http://www.itdecent.cn/p/1f2db76fd8d8
}

// ?? 對(duì)于非 `Promise` 對(duì)象,沒必要使用 `await` 關(guān)鍵字。

還有一種情況,就是 await 關(guān)鍵字后面接了一個(gè) thenable 對(duì)象,那么 await 會(huì)將其當(dāng)做是 Promise 對(duì)象。thenable 對(duì)象是指該對(duì)象上實(shí)現(xiàn)了 then() 方法(本身或原型上均可)。

const thenable = {
  then(resolve, reject) {
    resolve('abc')
    // 注意,then 方法中要使用 resolve 或 reject 去改變狀態(tài),
    // 否則 await thenable 的狀態(tài)一直會(huì)是 pending,
    // 由于 await 一直未等到 thenable 對(duì)象的狀態(tài)發(fā)生變化,
    // 因此 foo() 返回的 Promise 對(duì)象也將永遠(yuǎn)停留在 pending 狀態(tài),
    // 它只能苦苦地等待有朝一日 thenable 狀態(tài)能發(fā)生改變
  }
}

async function foo() {
  return await thenable // await 會(huì)把 thenable 當(dāng)作一個(gè) Promise 對(duì)象去處理
}

foo().then(res => {
  console.log(res) // "abc"
})

一旦遇到 await 后面的 Promise 對(duì)象為 rejected 狀態(tài)的情況,會(huì)終止后面代碼的執(zhí)行。例如:

async function foo() {
  await Promise.reject('some errors...')
  let a = 'any' // 這一行及后面的代碼,并不會(huì)被執(zhí)行
  return a
}

foo().catch(err => {
  console.warn(err) // "some errors..."
})

這些異常都可以使用 try...catch 去捕獲,下面會(huì)講到。

2. 錯(cuò)誤處理

async 函數(shù)的語法規(guī)則總體上比較簡單,難點(diǎn)是錯(cuò)誤處理機(jī)制。

前面也提到過,一旦 async 函數(shù)內(nèi)部某個(gè) Promise 對(duì)象狀態(tài)變?yōu)?rejected,或者存在語法錯(cuò)誤,或者主動(dòng)拋出錯(cuò)誤,會(huì)停止執(zhí)行函數(shù)體內(nèi)部的余下代碼,并使得 async 函數(shù)返回的 Promise 對(duì)象改變狀態(tài) rejected。

舉個(gè)例子:

async function foo() {
  await Promise.reject('error') // 表示一個(gè)異步操作
  let a = 'any'
  return a
}

// 如何使其正常調(diào)用 then 方法
foo().then(res => {
  console.log(res) // "any"
})

假設(shè)異步操作的結(jié)果成功與否,不影響函數(shù)最終的結(jié)果,使其正常執(zhí)行到最后并返回結(jié)果,有幾種處理方式:

// 方式一:利用 try...catch 語句
async function foo() {
  try {
    await Promise.reject('error')
  } catch (e) {
    console.warn(e) // "error"
    // 錯(cuò)誤處理...
  }
  let a = 'any'
  return a
}
// 方式二:如果是 Promise 對(duì)象,可以用 catch 方法捕獲
async function foo() {
  await Promise.reject('error').catch(err => {
    console.warn(err) // "error"
    // 錯(cuò)誤處理...
    // 只要這里面不再拋出錯(cuò)誤,await 拿到的 Promise 對(duì)象狀態(tài)為 fulfilled
  })
  let a = 'any'
  return a
}

以上示例中,只有一個(gè)異步操作,這種情況下也可以直接采用 Promise 的寫法去處理。一般情況下,使用到 async/await 的寫法,表示函數(shù)體內(nèi)部會(huì)存在多個(gè)異步操作,通常的錯(cuò)誤處理方式是:利用一個(gè) try...catch 語句將整個(gè)函數(shù)體包裹起來。

async function request() {
  try {
    await fetch('/user/1')
    await fetch('/user/2')
    await fetch('/user/3')
  } catch (e) {
    // 捕獲異常,并做錯(cuò)誤處理
  }
}

四、Async 函數(shù)應(yīng)用

1. 實(shí)現(xiàn)重復(fù)請求

此前另一篇文章提到過,可以 async 函數(shù)結(jié)合 for 循環(huán)、try...catch 可以實(shí)現(xiàn)多次重復(fù)嘗試的效果。

async function request(url) {
  let res
  let err
  const MAX_NUM_RETRIES = 3

  for (let i = 0; i < MAX_NUM_RETRIES; i++) {
    try {
      res = await fetch(url).then(response => response.json())
      break
    } catch (e) {
      err = e
      // Do nothing and make it continue.
    }
  }

  if (res) return res
  throw err
}

request('/user/1')
  .then(res => {
    console.log('success')
  })
  .catch(err => {
    console.log('fail')
  })
2. 不要在 forEach 中使用 async/await

它可能得不到預(yù)期結(jié)果,在另一篇文章也詳細(xì)地分析過了。

如果使用 promiseasync 函數(shù)作為 forEach() 等類似方法的 callback 參數(shù),最好對(duì)造成的執(zhí)行順序影響多加考慮,否則容易出現(xiàn)錯(cuò)誤。

舉個(gè)例子,打印 sum 并不會(huì)得到預(yù)期的結(jié)果 6,而是 3。

// PS:實(shí)際場景肯定不會(huì)這樣去求和,這里只是為了舉例而舉例
function foo() {
  let sum = 0
  const arr = [1, 2, 3]
  const sumFn = (a, b) => a + b

  arr.forEach(async item => {
    sum = await sumFn(sum, item)
  })

  setTimeout(() => {
    console.log(sum) // 3
    // 注意,這里打印不能去掉 setTimeout,否則打印結(jié)果永遠(yuǎn)是 0。
  })
}

foo()

由于 await anything 表達(dá)式(這里 anything 表示任意值)相當(dāng)于使用了 Promise.resolve()anything 包裹起來,其中 Promise 屬于異步任務(wù)(微任務(wù)),它會(huì)在同步任務(wù)執(zhí)行完之后才會(huì)去執(zhí)行。

當(dāng)執(zhí)行第一次循環(huán),先執(zhí)行 forEach 的回調(diào)函數(shù),遇到 await sumFn(sum, 1),會(huì)執(zhí)行 sumFn 函數(shù)計(jì)算結(jié)果,所以變成了 sum = await 1。由于是異步,會(huì)暫時(shí) Hold 將其放入微任務(wù)隊(duì)列,因此 sum 暫時(shí)不會(huì)被重新賦值,它仍是 0;接著執(zhí)行下一次循環(huán),同理變成 sum = await 2,又被 Hold ??;再下一次循環(huán)同理變成 sum = await 3。至此 forEach 的三次回調(diào)函數(shù)執(zhí)行完畢,接著繼續(xù)往下走,遇到 setTimeout(屬于異步任務(wù)中的宏任務(wù)),由于延遲時(shí)間為 0 會(huì)直接放入任務(wù)隊(duì)列,它將會(huì)在下一次宏任務(wù)中執(zhí)行。

至此,同步任務(wù)已執(zhí)行完畢,緊接著,會(huì)依次執(zhí)行剛剛被 Hold 住的三個(gè)微任務(wù)(分別是 sum = 1、sum = 2sum = 3),因此 sum 變成了 3。微任務(wù)執(zhí)行完畢之后,(由于本示例中沒有 UI Render)立刻會(huì)執(zhí)行下一次宏任務(wù),即 console.log(sum),因此打印結(jié)果為 3

其實(shí)理解原理之后,分析這道題就很簡單了。那么替代方案就是使用 for...of 語句:

async function foo() {
  let sum = 0
  const arr = [1, 2, 3]
  const sumFn = (a, b) => a + b

  for (let item of arr) {
    sum = await sumFn(sum, item)
  }

  setTimeout(() => {
    console.log(sum) // 6
    // 改成 for...of 之后,這里可以去掉 setTimeout 了,
    // 直接將 console 語句放到外面,也可以按順序執(zhí)行了
  })
}

foo()

那為什么 for...of 就可以,因?yàn)樗举|(zhì)上就是一個(gè) while 循環(huán)。

async function foo() {
  let sum = 0
  const arr = [1, 2, 3]
  const sumFn = (a, b) => a + b

  // for (let item of arr) {
  //   sum = await sumFn(sum, item)
  // }

  // 相當(dāng)于
  const iterator = arr[Symbol.iterator]()
  let iteratorResult = iterator.next()
  while (!iteratorResult.done) {
    sum = await sumFn(sum, iteratorResult.value)
    iteratorResult = iterator.next()
  }

  setTimeout(() => {
    console.log(sum) // 6
  })
}

foo()
3. 繼發(fā)關(guān)系

文章前面部分提到過 Async 函數(shù)中使用 await 去控制異步操作,看起來像 Promise.all(),但又有區(qū)別。

如下示例:

async function request() {
  // 假設(shè) fetchUser1、fetchUser2、fetchUser3 表示異步請求
  let user1 = await fetchUser1()
  let user2 = await fetchUser2()
  let user3 = await fetchUser3()
  return 'abc'
}

上面示例中,request() 函數(shù)的返回值不依賴于 fetchUser 的結(jié)果,而且三個(gè) fetchUser 請求是相互獨(dú)立的。如果按上面的寫法,直接影響程序的執(zhí)行時(shí)間。

因?yàn)槟壳袄^發(fā)式寫法,fetchUser2 請求只有在 fetchUser1 請求完成并返回?cái)?shù)據(jù)后才會(huì)被發(fā)出(fetchUser3 同理)。但根據(jù)代碼邏輯來看,它們是沒有關(guān)聯(lián)關(guān)系的,為什么不一次性連續(xù)發(fā)出三個(gè)請求以減少整個(gè)程序的執(zhí)行時(shí)間呢?

因此,我們可以優(yōu)化一下。

// 寫法一(推薦)
async function request() {
  // 這樣 fetchUser1、fetchUser2、fetchUser3 將會(huì)同時(shí)發(fā)出請求
  // 這也是文中所說 await 與 Promise.all() 的不同點(diǎn)。
  let [user1, user2, user3] = await Promise.all([
    fetchUser1(),
    fetchUser2(),
    fetchUser3()
  ])
  return 'abc'
}

// 寫法二
async function request() {
  let p1 = fetchUser1()
  let p2 = fetchUser2()
  let p3 = fetchUser3()
  let user1 = await p1
  let user2 = await p2
  let user3 = await p3
  return 'abc'
}

五、Async 函數(shù)的實(shí)現(xiàn)原理

Async 函數(shù)的實(shí)現(xiàn)原理,就是將 Generator 函數(shù)和自動(dòng)執(zhí)行器,包裝在一個(gè)函數(shù)里面。

如果對(duì) Generator 函數(shù)不熟悉的話,建議先看下這篇文章:細(xì)讀 ES6 之 Generator 生成器,再回來看本節(jié)內(nèi)容。

舉個(gè)例子,下面 requestasync 函數(shù)。

function delay(time) {
  return new Promise(resolve => setTimeout(resolve, time))
}

async function request() {
  let response = await fetch('http://192.168.1.117:7701/config')
  let result = await response.json()
  await delay(1000)
  await delay(2000)
  await delay(3000)
  return result
}

既然 Async 函數(shù)是 Generator 函數(shù)和自動(dòng)執(zhí)行器的結(jié)合,那么相當(dāng)于將上面 Async 函數(shù)中的 asyncawait 關(guān)鍵字,就替換成 Generator 函數(shù)的 *yield,外加一個(gè)自動(dòng)執(zhí)行器。

于是變成了這樣:

function* request() {
  let response = yield fetch('http://192.168.1.117:7701/config')
  let result = yield response.json()
  yield delay(1000)
  yield delay(2000)
  yield delay(3000)
  return result
}

那么怎樣實(shí)現(xiàn)執(zhí)行器,才能達(dá)到 Async 函數(shù)的語義呢?

其實(shí)沒那么難,只要弄清楚 Generator.prototype.next()Generator.prototype.throw() 兩個(gè)方法就沒太大問題了。當(dāng)然前提還是要知道生成器的執(zhí)行過程。

實(shí)現(xiàn)如下:

function executor(genFn, ...args) {
  return new Promise((resolve, reject) => {
    if (Object.prototype.toString.call(genFn) !== '[object GeneratorFunction]') {
      return reject(new Error('Must be a generator function!'))
    }

    const gen = genFn(...args)

    const step = iteratorResult => {
      console.count('次數(shù)')
      const { done, value } = iteratorResult

      if (done) {
        return resolve(value)
      }

      Promise
        .resolve(value)
        .then(res => {
          // 關(guān)于捕獲異常,怕有人不懂,這里解釋一下:
          // 假設(shè)調(diào)用 next 方法后,Generator 內(nèi)部報(bào)錯(cuò),且內(nèi)部未進(jìn)行捕獲錯(cuò)誤時(shí),
          // 內(nèi)部的錯(cuò)將會(huì)被 Generator 外部的 try...catch 捕獲到(即這里的 then 回調(diào)函數(shù)),
          // 但是這里不進(jìn)行捕獲的原因是:使其傳遞出去,讓后面的 Promise.prototype.catch() 方法捕獲,
          // 進(jìn)而在 catch() 的回調(diào)函數(shù)內(nèi)調(diào)用 Generator.prototype.throw() 主動(dòng)使得生成器結(jié)束,并 reject 錯(cuò)誤。
          step(gen.next(res))
        })
        .catch(err => {
          try {
            gen.throw(err)
          } catch (e) {
            reject(e)
          }
        })
    }

    step(gen.next())
  })
}

調(diào)用方式如下:

function* request() {
  let response = yield fetch('http://192.168.1.117:7701/config')
  let result = yield response.json()
  yield delay(1000)
  yield delay(2000)
  yield delay(3000)
  return result
}

// 語法:executor(generatorFunction[, param[, ... param]])
// 其中第一個(gè)參數(shù)必須是生成器函數(shù),若生成器函數(shù)需要傳遞參數(shù),往后面添加即可
executor(request)
  .then(res => {
    console.log(res)
  })
  .catch(err => {
    console.warn(err)
  })

大致實(shí)現(xiàn)如上。如果還是不太懂的,應(yīng)該是 Generator 函數(shù)這塊知識(shí)點(diǎn)還不夠熟悉。

如果這樣,還是先要看下這篇文章熟悉相關(guān)知識(shí),而且文章末尾很相似的自動(dòng)執(zhí)行器的實(shí)現(xiàn)示例。

六、總結(jié)

前面寫過關(guān)于 Iterator 、Promise、Generator 相關(guān)內(nèi)容的文章,再到本文的 Async 函數(shù),其實(shí)都是環(huán)環(huán)相扣的,因此應(yīng)按順序來學(xué)習(xí),cai'n。

其中 Promise 雖然解決了“橫向”回調(diào)地獄(Callback Hell)的問題,但是又出現(xiàn)了“縱向”不斷 then、catch 的處理。

而 Generator 函數(shù)提出了一種全新的異步控制的方案,調(diào)用 Generator 函數(shù)不會(huì)自動(dòng)執(zhí)行其函數(shù)體內(nèi)部的代碼,僅返回一個(gè)生成器對(duì)象,需要我們手動(dòng)去調(diào)用生成器的 next() 方法以執(zhí)行內(nèi)部的代碼。盡管生成器對(duì)象實(shí)現(xiàn)了 Iterator 接口,因而可供 for...of 等使用,但是往往結(jié)合網(wǎng)絡(luò)請求等異步操作時(shí),用 for...of 等語句幾乎是不能滿足我們需求的。這種前提下,需要我們自實(shí)現(xiàn)一個(gè)執(zhí)行器來自動(dòng)調(diào)用 Generator 函數(shù)實(shí)例。但要我們自個(gè)實(shí)現(xiàn)???這就是最大的問題了(手動(dòng)狗頭)。

基于這種狀況下,著名的 co 模塊就實(shí)現(xiàn)了 Generator 執(zhí)行器,以供我們使用。但要另外引入第三方庫,這本身也是個(gè)問題,哈哈...??

基于以上種種不盡人意,于是 TC39 被開發(fā)者各種罵,說能不能原生實(shí)現(xiàn)一個(gè) Generator 的執(zhí)行器啊,現(xiàn)在用得很不爽??!TC39 委員會(huì)那波人終究承受不住各方壓力,于是只能加班幫我們實(shí)現(xiàn)了...

千呼萬喚始出來,終于在 ES2017 標(biāo)準(zhǔn)中引入了 Async 函數(shù),它就是實(shí)打?qū)嵉?Generator 語法糖啊。除了將 Generator 中的 *yield 換成了 asyncawait 之外,還結(jié)合了 Promise。既解決了 Generator 的執(zhí)行問題,Promise 的“縱向發(fā)展”問題。皆大歡喜了,TC39 委員會(huì)大佬們心想,終于可以消停了,又可以日常摸魚了。

The end.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 異步編程對(duì)JavaScript語言太重要。Javascript語言的執(zhí)行環(huán)境是“單線程”的,如果沒有異步編程,根本...
    呼呼哥閱讀 7,404評(píng)論 5 22
  • 前面的話 ES2017標(biāo)準(zhǔn)引入了async 函數(shù),使得異步操作變得更加方便。本文將詳細(xì)介紹async函數(shù) 概述 a...
    CodeMT閱讀 1,388評(píng)論 0 3
  • 含義 async函數(shù)是Generator函數(shù)的語法糖,它使得異步操作變得更加方便。 寫成async函數(shù),就是下面這...
    oWSQo閱讀 2,041評(píng)論 0 2
  • 1、含義 在ES2017標(biāo)準(zhǔn)中引入了async函數(shù),使得一步操作變得更加方便。那么async函數(shù)是什么?一句話,它...
    24KBING閱讀 374評(píng)論 0 0
  • 1. 含義 async函數(shù)是什么?一句話,它就是 Generator 函數(shù)的語法糖。async函數(shù)就是將 Gene...
    MrZhou_b216閱讀 317評(píng)論 0 0

友情鏈接更多精彩內(nèi)容