"Code tailor",为前端开发者提供技术相关资讯以及系列基础文章,微信关注“小和山的菜鸟们”公众号,及时获取最新文章。
前言
在开始学习之前,我们想要告诉您的是,本文章是对阮一峰《ECMAScript6 入门》一书中 "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()
方法返回一个包含属性done
和value
的对象。该方法也可以通过接受一个参数用以向生成器传值
返回的对象包含两个属性:
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()
方法用来向生成器抛出异常,并恢复生成器的执行,返回带有done
及value
两个属性的对象
done
(布尔类型)- 如果迭代器已经返回了迭代序列的末尾,则值为
true
。在这种情况下,可以指定迭代器value
的返回值。 - 如果迭代能够继续生产在序列中的下一个值,则值为
false
。 这相当于不指定done
属性的值。 value
- 迭代器返回的任何JavaScript
值。当done
是true
的时候可以省略。
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
and10, 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
anda
- B:
a
andundefined
- C:
['a', 'b', 'c']
anda
- 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*2
。i
等于 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()
则会持续返回b
和c
直到该数组调用结束。。
console.log(two.next().value) // 'a'
console.log(two.next().value) // 'b'
console.log(two.next().value) // 'c'
console.log(two.next().value) // undefined
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。