ChuChencheng / note

菜鸡零碎知识笔记
Creative Commons Zero v1.0 Universal
3 stars 0 forks source link

ES6 async await #30

Open ChuChencheng opened 4 years ago

ChuChencheng commented 4 years ago

概念

ES2017 引入的 async, await 实际上是 Generator 函数的语法糖,使异步操作的写法更加方便。

(既然是语法糖,那我们就可以尝试用 Generator 自己去手写一个 async, await 了 😆 )

跟 Generator 函数比较,可以发现,语法上,就是把 * 号换成 asyncyield 关键字换成 await 而已。但是,肯定不只是替换关键字这么简单,不然为何不用 Generator 。

对 Generator 的改进

  1. 内置执行器,不用像 Generator 函数返回迭代器那种模式那样,手动去执行 next
  2. 更好的语义
  3. async 函数的返回值是 Promise

实现一个 async

假设有以下代码:

function getAsyncValue () {
  return new Promise((resolve, reject) => {
    setTimeout(resolve, 10, 123)
  })
}

async function asyncFunction (p) {
  console.log('param: ', p)
  const value = await getAsyncValue()
  return value
}

asyncFunction(666).then((result) => {
  console.log(result)
})

// param: 666
// 123

如果只依赖 Generatoryield 语法,不使用 async, await ,但要实现同样效果的话,首先把 await 替换成 yield ,然后实现 async 包装:

const _async = (generator) => {
  return function (...args) {
    // async 函数返回一个 Promise
    return new Promise((resolve, reject) => {
      // Promise 内部会自动将 Generator 函数返回的迭代器执行到最后
      const it = generator(...args)
      const step = (nextFunction) => {
        let next
        try {
          next = nextFunction()
        } catch (e) {
          reject(e)
        }
        if (next.done) {
          return resolve(next.value)
        }
        // 其中, next 的执行时机就是 yield 后面的 Promise 状态改变的时候
        // next 的参数就是上一个 yield 的值
        Promise.resolve(next.value).then((value) => {
          return step(() => it.next(value))
        }, (reason) => {
          return step(() => it.throw(reason))
        })
      }
      step(() => it.next())
    })
  }
}

最后就可以改写成非语法糖的 Generator 语法形式:

function getAsyncValue () {
  return new Promise((resolve, reject) => {
    setTimeout(resolve, 10, 123)
  })
}

const asyncFunction = _async(function* (p) {
  console.log('param: ', p)
  const value = yield getAsyncValue()
  return value
})

asyncFunction(666).then((result) => {
  console.log(result)
})

// param: 666
// 123

参考

http://es6.ruanyifeng.com/#docs/async

ChuChencheng commented 4 years ago

scriptoj 上的题目: #72 使用 generator 模拟 async/await