Generator 是 ES6 的新规范,它属于函数的一部分,常用于处理异步代码,本篇文章将向大家简单介绍一下 Generator ,如有错误,欢迎大家批评指正哈!
1.一个简单的Generator
这是一个简单的 Generator 例子:
function* gen() {
return 'first generator';
}
let generatorResult = gen();
使用 * 表示这是一个Generator函数
我们在控制台打印一下 generatorResult ,结果是:
console.log(generatorResult)
[object Generator]: {}
__proto__: Object
undefined
这说明 generatorResult 不是一个普通的函数,而是一个 Generator 原型的实例,我们需要调用该实例的next方法取值,next 返回的对象中包含了 value 属性
generatorResult.next()
[object Object]: {done: true, value: "first generator"}
注意事项:
- 不能无限制地调用 next 从 Generator 中取值,Generator 如同序列,一旦序列中的值被消费就不能再次消费它
- 为了能够再次消费该序列,需要创建另一个 Generator 实例
generatorResult.next().value // 第一次取值
"first generator"
generatorResult.next()
[object Object]: {done: true, value: undefined}
generatorResult.next().value //第二次取值
undefined
2.yield关键字
在 Generator 函数中有一个新的关键字,称为 yield
一个简单的 Generator 序列:
function* genSequence() {
yield 'first';
yield 'second';
yield 'third'
}
let generatorSequence = genSequence();
控制台输出:
generatorSequence.next()
[object Object]: {done: false, value: "first"}
generatorSequence.next().value
"second"
generatorSequence.next().value
"third"
通过上面的操作我们可以总结出:
- yield 使 Generator 函数暂停了执行并将结果返回给调用者
- 调用一次后,yield 将函数置于暂停模式并返回值,而且还准确地记住了暂停的位置
- 带有 yield 的 Generator 都会以惰性求值的顺序执行(当我们需要时,相应的值才会被返回)
3.done 属性
done属性清楚地告诉我们 Generator 序列的消费情况
generatorSequence.next()
[object Object]: {done: false, value: "first"}
generatorSequence.next().value
"second"
generatorSequence.next().value
"third"
generatorSequence.next().value
undefined
generatorSequence.next()
[object Object]: {done: true, value: undefined}
- done 为true 表示 Generator 序列已经完全消费了
- done 为false 表示还未消费完
4.向 Generator 传递数据
创建一个新的函数和 Generator 实例 :
function* sayName() {
var firstName = yield;
var secondName = yield;
console.log(firstName + secondName)
}
let fullName = sayName();
然后我们在控制台输入:
fullName.next()
[object Object]: {done: false, value: undefined}
fullName.next('chen')
[object Object]: {done: false, value: undefined}
fullName.next('xiansheng')
chenxiansheng
[object Object]: {done: true, value: undefined}
是不是很神奇呢!下面说一下执行过程:
第一次调用 next 时,代码将返回并暂停于:
var firstName = yield;
由于没有通过 yield 发送任何值,next 将返回 undefined
第二次调用 next 时, Generator 将从上一次暂停的状态恢复,即这一行:
var firstName = yield;
由于本次调用 next 时传入了值 ‘chen’, yield 将被 chen 替换,因此 firstName 的值变为 chen
赋值后,代码继续执行,直到再次遇到 yield 并在此处暂停:
var secondName = yield;
第三次调用 next 时,原理和第二次相同,传入的 xiansheng 将替换 yield 并赋值于 secondName,接着执行:
console.log(firstName + secondName)
所以控制台将打印出 chenxiansheng
5.使用 Generator 处理异步调用
看下面的代码例子:
let generator;
let getDataOne = () => {
setTimeout(function () {
// 调用Generator并传递数据
generator.next('dummy data one')
}, 1000);
}
let getDataTwo = () => {
setTimeout(function () {
generator.next('dummy data two')
}, 1000);
}
function* main() {
let dataOne = yield getDataOne();
let dataTwo = yield getDataTwo();
console.log("data one",dataOne)
console.log("data two",dataTwo)
}
// 创建一个Generator实例
generator = main();
在控制台输入 generator.next() 触发 main 函数执行,将打印出以下结果:
generator.next()
[object Object]: {done: false, value: undefined}
data one dummy data one // 两秒后打印出
data two dummy data two
main 代码看上去像在同步地调用getDataOne和getDataTwo,但两个调用是异步进行的
下面简单说一下整个运行过程:
首先,我们用 generator.next() 调用 main 函数执行,并遇到了第一个 yield :
let dataOne = yield getDataOne();
现在 Generator 进入了暂停模式,但是在暂停之前,它调用了 getDataOne 函数:
let getDataOne = () => {
setTimeout(function () {
// 调用Generator并传递数据
generator.next('dummy data one')
}, 1000);
}
因此,当 Generator 暂停时, getDataOne 函数开始执行,这时,时间将会从1000倒数至0,也就是一秒后,将会执行下面这行:
generator.next('dummy data one')
这将会向 yield 语句返回 dummy data one ,因此 dataOne 变量变为 dummy data one ,一旦 dataOne 被赋值,代码将会继续执行到下一行:
let dataTwo = yield getDataTwo();
同理,当这行执行完毕后,我们得到了 dataOne 和 dataTwo :
dataOne = dummy data one
dataTwo = dummy data two
因为我们在 getDataOne 和 getDataTwo 各设置了1000毫秒后执行,所以控制台将会在两秒后同时打印出结果:
data one dummy data one
data two dummy data two
以上就是我要介绍的全部内容了,小伙伴们学习愉快!