Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JavaScript之generator函数的自动执行 #11

Open
chenyong9528 opened this issue Aug 7, 2020 · 0 comments
Open

JavaScript之generator函数的自动执行 #11

chenyong9528 opened this issue Aug 7, 2020 · 0 comments

Comments

@chenyong9528
Copy link
Owner

chenyong9528 commented Aug 7, 2020

关于generator函数

generator在《ECMAScript 6 入门》中的定义:

Generator 函数是一个状态机,封装了多个内部状态。执行 Generator 函数会返回一个遍历器对象(Iterator),也就是说,Generator 函数除了状态机,还是一个遍历器对象生成函数。返回的遍历器对象,可以依次遍历 Generator 函数内部的每一个状态。

简单看看它的使用:

function* gen(n) {
  const x = yield n + 5
  const y = yield x + 5
  return y
}

const it = gen(5)

it.next() // { value: 10, done: false }
it.next(10) // { value: 15, done: false }
it.next(20) // { value: 20, done: true }
it.next() // { value: undefined, done: true }

值得注意的几个地方:

  1. 调用generator函数并不会执行其中的代码,而是返回一个遍历器对象
  2. 调用遍历器对象next方法generator函数才开始执行,并停在第一个yield的地方,再次调用next,函数恢复执行,停在下一个yield,如此直到函数执行完毕
  3. next方法的返回值是一个对象,该对象value属性是yield后面的内容
  4. next方法的参数,是上下一个yield的返回值,这使得我们可以不停向generator函数中传递数据
  5. 可以有多个yield,但只能有一个return

当然,在我们平时使用中,yield后面一般会是Promise

generator函数对我们来说其实并不陌生,async作为generator的语法糖,使用async其实间接的使用了generator,实现generator的自动执行,就是为了帮我们更好的了解async函数的工作方式。在async之前,就已经出现了类似的解决方案,如co模块,它能够实现generator的自动执行,后来将它纳入了标准中,所以,我们要实现的,其实就是co模块。

关于generator的更多细节可以参考阮一峰老师的《ECMAScript 6 入门》

实现

我们的目的在于了解generator函数自动执行的核心逻辑,所以,要实现的是理想状态下的co模块myco,它接收generator函数作为参数

const promise = function(v) {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve(v)
    }, 1000)
  })
}

const gen = function* () {
  const res1 = yield promise(5)
  console.log(res1)
  const res2 = yield promise(res1 + 5)
  console.log(res2)
  return (res1 + res2)
}

myco(gen)

第一版:

function myco(gen) {
  const it = gen()

  const { value, done } = it.next()

  value.then(res => {
    const { value, done } = it.next(res)

    value.then(res => {
      const { value, done } = it.next(res)

      if (done) {
        // 结束
      }
    })
  })
}

myco(gen)

// 1s后 5
// 2s后 10

以上代码中,第一次调用next(),函数gen开始执行,遇到yield暂停执行并返回它后面的promise,该promiseresolve的时候再次调用next(),函数gen恢复执行,遇到第二个yield再次暂停并返回后面的值,如此往复,直到gen函数执行完毕。理解这个过程,我们可以将myco改成递归的方式

注意:next()的参数会成为上一个yield的返回值,这样的好处在于我们可以不停向函数gen中注入值,这种机制使我们可以以同步的方式来表达异步操作,这样更易于理解

第二版:

function myco(gen) {
  const it = gen()

  function step(v) {
    const { value, done } = it.next(v)

    if (done) return

    value.then(res => {
      step(res)
    })
  }

  step()
}

最后,myco函数会返回一个Promise,该Promise的状态会在gen函数执行完毕的时候resolve

最终版:

function myco(gen) {
  return new Promise(resolve => {
    const it = gen()

    function step(v) {
      const { value, done } = it.next(v)

      if (done) return resolve(value)

      value.then(res => {
        step(res)
      })
    }

    step()
  })
}

myco(gen).then(res => {
  console.log(res)
})

// 1s后 5
// 2s后 10 15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant