logo

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

2019-09-05

JiangRen Mr

虽然现在使用 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。
logo

Follow Us

linkedinfacebooktwitterinstagramweiboyoutubebilibilitiktokxigua

We Accept

/image/layout/pay-paypal.png/image/layout/pay-visa.png/image/layout/pay-master-card.png/image/layout/pay-airwallex.png/image/layout/pay-alipay.png

地址

Level 10b, 144 Edward Street, Brisbane CBD(Headquarter)
Level 2, 171 La Trobe St, Melbourne VIC 3000
四川省成都市武侯区桂溪街道天府大道中段500号D5东方希望天祥广场B座45A13号
Business Hub, 155 Waymouth St, Adelaide SA 5000

Disclaimer

footer-disclaimerfooter-disclaimer

JR Academy acknowledges Traditional Owners of Country throughout Australia and recognises the continuing connection to lands, waters and communities. We pay our respect to Aboriginal and Torres Strait Islander cultures; and to Elders past and present. Aboriginal and Torres Strait Islander peoples should be aware that this website may contain images or names of people who have since passed away.

匠人学院网站上的所有内容,包括课程材料、徽标和匠人学院网站上提供的信息,均受澳大利亚政府知识产权法的保护。严禁未经授权使用、销售、分发、复制或修改。违规行为可能会导致法律诉讼。通过访问我们的网站,您同意尊重我们的知识产权。 JR Academy Pty Ltd 保留所有权利,包括专利、商标和版权。任何侵权行为都将受到法律追究。查看用户协议

© 2017-2024 JR Academy Pty Ltd. All rights reserved.

ABN 26621887572