异步—手写 async_await

前言

如果让你手写 async 函数的实现,你是不是会觉得很复杂?这篇文章带你用 20 行搞定它的核心。

经常有人说 async 函数是 generator 函数的语法糖,那么到底是怎么样一个糖呢?让我们来一层层的剥开它的糖衣。

有的同学想说,既然用了 generator 函数何必还要实现 async 呢?

这篇文章的目的就是带大家理解清楚 async 和 generator 之间到底是如何相互协作,管理异步的。

示例

const getData = () =>
  new Promise(resolve => setTimeout(() => resolve('data'), 1000))

async function test() {
  const data = await getData()
  console.log('data: ', data)
  const data2 = await getData()
  console.log('data2: ', data2)
  return 'success'
}

// 这样的一个函数 应该再1秒后打印data 再过一秒打印data2 最后打印success
test().then(res => console.log(res))

思路

对于这个简单的案例来说,如果我们把它用 generator 函数表达,会是怎么样的呢?

function* testG() {
  // await被编译成了yield
  const data = yield getData()
  console.log('data: ', data)
  const data2 = yield getData()
  console.log('data2: ', data2)
  return 'success'
}

我们知道,generator 函数是不会自动执行的,每一次调用它的 next 方法,会停留在下一个 yield 的位置。

利用这个特性,我们只要编写一个自动执行的函数,就可以让这个 generator 函数完全实现 async 函数的功能。

const getData = () =>
  new Promise(resolve => setTimeout(() => resolve('data'), 1000))
var test = asyncToGenerator(function* testG() {
  const data = yield getData()
  console.log('data', data)
  const data2 = yield getData()
  console.log('data2', data2)
  return 'success'
})
test().then(res => console.log(res))

那么大体上的思路已经确定了,

asyncToGenerator接受一个generator函数,返回一个promise

关键就在于,里面用yield来划分的异步流程,应该如何自动执行。

如果是手动执行

在编写这个函数之前,我们先模拟手动去调用这个generator函数去一步步的把流程走完,有助于后面的思考。

function* testG() {
  // await被编译成了yield
  const data = yield getData()
  console.log('data: ', data);
  const data2 = yield getData()
  console.log('data2: ', data2);
  return 'success'
}