logo
JR Academy · Blog职业洞察

如何写一个让面试官满意的 Generator 执行器?

虽然现在使用 async 函数 就可以替代 Generator 执行器了,不过了解下 Generator 执行器的原理还是挺有必要的。如果你不了解 Generator,那么你需要看这里。

发布日期
阅读时长5 分钟
作者

虽然现在使用 async 函数 就可以替代 Generator 执行器了,不过了解下 Generator 执行器的原理还是挺有必要的。

如果你不了解 Generator,那么你需要看这里

例子都可以在 Console 中运行的(谷歌版本 76.0.3809.100),都是以最新浏览器支持的 JavaScript 特性来编写的,不考虑兼容性。

术语区分

Generator 是 Generator function 运行后返回的对象。

执行器的原理

原理一

有 Generator next 函数的特性,next 函数运行后会返回如下结构:

{ value: 'world', done: false }

或者

{ value: undefined, done: true }

那么我们可以使用递归运行 next 函数,如果 done 为 true 则停止 next 函数的运行。

原理二

yield 表达式本身没有返回值,或者说总是返回 undefined。

next 方法可以带一个参数,该参数就会被当作上一个 yield 表达式的返回值。

由于 next 方法的参数表示上一个 yield 表达式的返回值,所以在第一次使用 next 方法时,传递参数是无效的。

function* test() {
  const a = yield "a"
  console.log(a)
  return true
}
const gen = test()
// 第一次 next() 会卡在 yield a 中,next 返回 {value: "a", done: false}
// 第一个 next() 函数的参数是无效的,无需传递
const nextOne = gen.next() 
// 传递参数 nextOne.value
// test 函数中的 console.log 输出 'a'
// 第二次的 next() 返回值为 {value: true, done: true}
gen.next(nextOne.value)

简单的执行器

简单的执行器代码如下:

/**
 * generator 执行器
 * @param {Function} func generator 函数
 * @return {Promise} 返回一个 Promise 对象
 */
function generatorExecuter(func) {
  const gen = func()

  function recursion(prevValue) {
    const next = gen.next(prevValue)
    const value = next.value
    const done = next.done
    if (done) {
      return Promise.resolve(value)
    } else {
      return recursion(value)
    }
  }
  return recursion()
}

代码并不复杂,最简单的执行器就出来了。如果你是一步一步的看文章过来的,都理解了原理,那么这些代码也很好理解。

考虑 yield 的类型是 Promise

上面的代码执行如下的 Generator 函数是不正确的:

function* test() {
  const a = yield Promise.resolve('a')
  console.log(a)
  return true
}
generatorExecuter(test)

运行上面的代码后,test 函数 console.log 输出的不是 a,而是 Promise {<resolved>: "a"}

那么我们代码需要这样处理:

/**
 * generator 执行器
 * @param {Function} func generator 函数
 * @return {Promise} 返回一个 Promise 对象
 */
function generatorExecuter(func) {
  const gen = func()

  function recursion(prevValue) {
    const next = gen.next(prevValue)
    const value = next.value
    const done = next.done
    if (done) {
      return Promise.resolve(value)
    } else {
      return Promise.resolve(value).then(finalValue => {
        return recursion(finalValue)
      })
    }
  }
  return recursion()
}

这样就再运行 generatorExecuter(test) 就没问题了。

考虑 yield 的类型是 Generator 函数

上面的代码执行如下的 Generator 函数是不正确的:

function* aFun() {
  return 'a'
}

function* test() {
  const a = yield aFun
  console.log(a)
  return true
}
generatorExecuter(test)

运行上面的代码后,test 函数 console.log 输出的不是 a,而是输出下面的字符串:

ƒ* aFun() {
  return 'a'
}

那么我们代码需要这样处理:

/**
 * 是否是 generator 函数
 */
function isGerneratorFunction(target) {
  if (
    Object.prototype.toString.apply(target) === '[object GeneratorFunction]'
  ) {
    return true
  } else {
    return false
  }
}

/**
 * 运行 generator
 * @param {GeneratorFunction} generator generator函数运行
 */
function generatorExecuter(generatorFunc) {
  const generator = generatorFunc()
  function recursionGeneratorNext(prevValue) {
    const next = generator.next(prevValue)
    let value = next.value
    const done = next.done

    if (isGerneratorFunction(value)) {
      value = generatorExecuter(value)
    }

    if (done) {
      return Promise.resolve(value)
    } else {
      // value 可能是 Promise 对象
      return Promise.resolve(value).then(finalValue => {
        return recursionGeneratorNext(finalValue)
      })
    }
  }
  return recursionGeneratorNext()
}

这样就再运行 generatorExecuter(test) 就没问题了。

考虑 yield 的类型是 Generator

上面的代码执行如下的 Generator 函数是不正确的:

function* aFun(value) {
  return value
}

function* test() {
  const a = yield aFun('a')
  console.log(a)
  return true
}
generatorExecuter(test)

运行上面的代码后,test 函数 console.log 输出的不是 a,而是 aFun {<suspended>}

那么我们代码需要这样处理:

/**
 * 是否是 generator
 */
function isGernerator(target) {
  if (Object.prototype.toString.apply(target) === '[object Generator]') {
    return true
  } else {
    return false
  }
}

/**
 * 是否是 generator 函数
 */
function isGerneratorFunction(target) {
  if (
    Object.prototype.toString.apply(target) === '[object GeneratorFunction]'
  ) {
    return true
  } else {
    return false
  }
}

/**
 * 运行 generator
 * @param {Generator} generator generator函数运行后返回的对象
 */
function runGernerator(generator) {
  function recursionGeneratorNext(prevValue) {
    const next = generator.next(prevValue)
    let value = next.value
    const done = next.done

    if (isGernerator(value)) {
      value = runGernerator(value)
    } else if (isGerneratorFunction(value)) {
      value = generatorExecuter(value)
    }

    if (done) {
      return Promise.resolve(value)
    } else {
      // value 可能是 Promise 对象
      return Promise.resolve(value).then(finalValue => {
        return recursionGeneratorNext(finalValue)
      })
    }
  }
  return recursionGeneratorNext()
}

/**
 * generator 执行器
 * @param {Generator || GeneratorFunction} generatorFunc generator 函数或者 generator
 * @return {Promise} 返回一个 Promise 对象
 */
function generatorExecuter(generatorFunc) {
  let generator
  if (isGerneratorFunction(generatorFunc)) {
    generator = generatorFunc()
  } else if (isGernerator(generatorFunc)) {
    generator = generatorFunc
  } else {
    throw new TypeError(
      'Expected the generatorFunc to be a GeneratorFuntion or Generator.'
    )
  }
  return runGernerator(generator)
}

这样就再运行 generatorExecuter(test) 就没问题了。

最终版

Generator 执行器没想的那么难,花点时间就可以吃透了。

/**
 * 是否是 generator
 */
function isGernerator(target) {
  if (Object.prototype.toString.apply(target) === '[object Generator]') {
    return true
  } else {
    return false
  }
}

/**
 * 是否是 generator 函数
 */
function isGerneratorFunction(target) {
  if (
    Object.prototype.toString.apply(target) === '[object GeneratorFunction]'
  ) {
    return true
  } else {
    return false
  }
}

/**
 * 运行 generator
 * @param {Generator} generator generator函数运行后返回的对象
 */
function runGernerator(generator) {
  function recursionGeneratorNext(prevValue) {
    const next = generator.next(prevValue)
    let value = next.value
    const done = next.done

    if (isGernerator(value)) {
      value = runGernerator(value)
    } else if (isGerneratorFunction(value)) {
      value = generatorExecuter(value)
    }

    if (done) {
      return Promise.resolve(value)
    } else {
      // value 可能是 Promise 对象
      return Promise.resolve(value).then(finalValue => {
        return recursionGeneratorNext(finalValue)
      })
    }
  }
  return recursionGeneratorNext()
}

/**
 * generator 执行器
 * @param {Generator || GeneratorFunction} generatorFunc generator 函数或者 generator
 * @return {Promise} 返回一个 Promise 对象
 */
function generatorExecuter(generatorFunc) {
  let generator
  if (isGerneratorFunction(generatorFunc)) {
    generator = generatorFunc()
  } else if (isGernerator(generatorFunc)) {
    generator = generatorFunc
  } else {
    throw new TypeError(
      'Expected the generatorFunc to be a GeneratorFuntion or Generator.'
    )
  }
  return runGernerator(generator)
}

运行例子如下,直接在谷歌 console 运行即可:

// 例子
function* one() {
  return 'one'
}

function* two() {
  return yield 'two'
}

function* three() {
  return Promise.resolve('three')
}

function* four() {
  return yield Promise.resolve('four')
}

function* five() {
  const a = yield new Promise(resolve => {
    setTimeout(() => {
      resolve('waiting five over')
    }, 1000)
  })
  console.log(a)
  return 'five'
}

function* all() {
  const a = yield one()
  console.log(a)
  const b = yield two()
  console.log(b)
  const c = yield three()
  console.log(c)
  const d = yield four()
  console.log(d)
  const e = yield five()
  console.log(e)
  return 'over'
}

generatorExecuter(all).then(value => {
  console.log(value)
})

// 或者
// generatorExecuter(all()).then(value => {
//   console.log(value)
// })
 
 
本文章转载自SegmentFault。
作者JiangRen Mr
一键分享或复制链接
JiangRen Mr
JiangRen Mr

匠人学院,全球华人学习 AI 第一站

查看该作者的更多文章 →

相关文章推荐

查看全部文章 →