5
头图
"Code tailor",为前端开发者提供技术相关资讯以及系列基础文章,微信关注“小和山的菜鸟们”公众号,及时获取最新文章。

前言

在开始学习之前,我们想要告诉您的是,本文章是对阮一峰《ECMAScript6 入门》一书中 "Generator" 章节的总结,如果您已掌握下面知识事项,则可跳过此环节直接进入题目练习

  • 什么是 Generator?
  • 如何创建和使用?
  • 如何在异步中应用?
  • 优缺点

如果您对某些部分有些遗忘,👇🏻 已经为您准备好了!

学习链接

Generator 的学习

Generator 的异步应用

汇总总结

概念

Generator函数是 ES6 中提供的一种异步编程解决方案。语法上,首先可以把它理解成,Generator 函数是一个状态机,封装了多个内部状态,需要使用next()函数来继续执行下面的代码。

创建和使用

function* helloWorldGenerator() {
  yield 'hello'
  yield 'world'
  return 'ending'
}
var hw = helloWorldGenerator()
hw.next()
// { value: 'hello', done: false }
hw.next()
// { value: 'world', done: false }
hw.next()
// { value: 'ending', done: true }
hw.next()
// { value: undefined, done: true }

常用方法

  • Generator.prototype.next()
next() 方法返回一个包含属性 donevalue 的对象。该方法也可以通过接受一个参数用以向生成器传值

返回的对象包含两个属性:

  • done (布尔类型)
  • 如果迭代器超过迭代序列的末尾,则值为 true。 在这种情况下,value 可选地指定迭代器的返回值。
  • 如果迭代器能够生成序列中的下一个值,则值为 false。 这相当于没有完全指定 done 属性。
  • value - 迭代器返回的任意的 Javascript 值。当 done 的值为 true 时可以忽略该值。
function* gen() {
  yield 1
  yield 2
  yield 3
}

var g = gen() // "Generator { }"
g.next() // "Object { value: 1, done: false }"
g.next() // "Object { value: 2, done: false }"
g.next() // "Object { value: 3, done: false }"
g.next() // "Object { value: undefined, done: true }"
  • Generator.prototype.return()
return() 方法返回给定的值并结束生成器
function* gen() {
  yield 1
  yield 2
  yield 3
}
var g = gen()
g.next() // { value: 1, done: false }
g.return('foo') // { value: "foo", done: true }
g.next() // { value: undefined, done: true }
  • Generator.prototype.throw()
throw() 方法用来向生成器抛出异常,并恢复生成器的执行,返回带有 donevalue 两个属性的对象
  • done (布尔类型)
  • 如果迭代器已经返回了迭代序列的末尾,则值为 true。在这种情况下,可以指定迭代器 value 的返回值。
  • 如果迭代能够继续生产在序列中的下一个值,则值为 false。 这相当于不指定 done 属性的值。
  • value - 迭代器返回的任何 JavaScript 值。当 donetrue 的时候可以省略。
function* gen() {
  while (true) {
    try {
      yield 42
    } catch (e) {
      console.log('Error caught!')
    }
  }
}

var g = gen()
g.next() // { value: 42, done: false }
g.throw(new Error('Something went wrong')) // "Error caught!"

在异步中的应用

var readFile = function (name, ms) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log(name + '读完了')
      resolve()
    }, ms)
  })
}

var gen = function* () {
  console.log('指定generator')
  yield readFile('first', 1000)
  yield readFile('second', 2000)
  return '完成了'
}

var g = gen()
var result = g.next()
result.value
  .then(() => {
    g.next()
  })
  .then(() => {
    g.next()
  })

优缺点

优点:

  • 可以控制函数的执行,可以配合 co 函数库使用

缺点:

  • 流程管理却不方便(即何时执行第一阶段、何时执行第二阶段)

更多知识请点击JavaScript 异步发展史

题目自测

一:Generator 函数的 yield 关键字的作用是()?

  • A: 停止执行
  • B: 退出函数
  • C: 暂停执行,等待 next()方法调用
  • D: 停止执行,可自行恢复执行

二:如下代码执行后打印结果为()

function* generator(i) {
  yield i
  yield i * 2
}

const gen = generator(10)

console.log(gen.next().value)
console.log(gen.next().value)
  • A: [0, 10], [10, 20]
  • B: 20, 20
  • C: 10, 20
  • D: 0, 10 and 10, 20

三:如下代码执行后打印结果为()

function* generatorOne() {
  yield ['a', 'b', 'c']
}

function* generatorTwo() {
  yield* ['a', 'b', 'c']
}

const one = generatorOne()
const two = generatorTwo()

console.log(one.next().value)
console.log(two.next().value)
  • A: a and a
  • B: a and undefined
  • C: ['a', 'b', 'c'] and a
  • D: a and ['a', 'b', 'c']

题目解析

一、

Answer:C

Generator 函数的调用方法与普通函数一样,也是在函数名后面加上一对圆括号。不同的是,调用 Generator 函数后,该函数并不执行,返回的也不是函数运行结果,而是一个指向内部状态的指针对象,也就是上一章介绍的遍历器对象(Iterator Object)。

下一步,必须调用遍历器对象的 next 方法,使得指针移向下一个状态。也就是说,每次调用 next 方法,内部指针就从函数头部或上一次停下来的地方开始执行,直到遇到下一个 yield 表达式(或 return 语句)为止。换言之,Generator 函数是分段执行的,yield 表达式是暂停执行的标记,而 next 方法可以恢复执行。


二、

Answer:C

常规函数不能在调用后中途停止。但是,生成器函数可以中途“停止”,然后从停止的地方继续。每次生成器函数遇到yield关键字时,该函数都会生成它后面指定的值。注意,在这种情况下,生成器函数不返回值,而是生成值。

首先,我们初始化生成函数,使 i 等于 10。我们使用next()方法调用生成器函数。第一次调用生成器函数时,i 等于 10。它遇到第一个 yield 关键字:它产生 i 的值。生成器现在是“暂停”的,10 被记录。

然后,我们使用next()方法再次调用该函数。它开始在之前停止的地方继续,仍然是 i 等于 10。现在,它遇到下一个yield关键字,并产生 i*2i 等于 10,所以它返回 10*2,也就是 20。结果是 10,20


三、

Answer:C

使用yield关键字,我们在生成器函数中产生值。 使用yield *关键字,我们可以从另一个生成器函数或可迭代对象(例如,数组)中产生值。 在generatorOne中,我们使用 yield 关键字产生整个数组['a','b','c']。 一个对象的下一个方法(one.next()。value)返回的对象的 value 属性值等于整个数组['a','b','c']

console.log(one.next().value) // ['a', 'b', 'c']
console.log(one.next().value) // undefined

generatorTwo中,我们使用yield *关键字。 这意味着我们得到的迭代器为数组['a','b','c']。 第一个产生的值为数组中的第一个值 a,因此我们第一次调用two.next().value时,将返回 a。后面继续调用next()则会持续返回bc直到该数组调用结束。。

console.log(two.next().value) // 'a'
console.log(two.next().value) // 'b'
console.log(two.next().value) // 'c'
console.log(two.next().value) // undefined

小和山的菜鸟们
377 声望2.1k 粉丝

每日进步的菜鸟,分享前端学习手册,和有心学习前端技术的小伙伴们互相探讨,一同成长。