1、入门
function* fn() {
console.log(1);
//暂停!
yield;
//调用next方法继续执行
console.log(2);
}
var iter = fn();
iter.next(); //1
iter.next(); //2
生成器解析如下:
1、函数生成器特点是函数名前面有一个‘*’
2、通过调用函数生成一个控制器
3、调用控制器的next()方法开始执行函数
4、遇到yield函数将暂停
5、再次调用控制器的next() 继续执行函数
yield关键词并不会像return一样立即结束函数,只是暂停这个生成器函数,直到下一次调用next方法会继续从yield位置向下执行。
generator的特点
-
每当执行完一条yield语句后函数就会自动停止执行, 直到再次调用next();
-
yield关键字只可在生成器内部使用,在其他地方使用会导致程序抛出错误
-
可以通过函数表达式来创建生成器, 但是不能使用箭头函数
-
可以在generator函数运行的不同阶段从外部内部注入不同的值,从而改变函数的行为
-
yield语句无返回值,总是返回undefined
-
next方法可以带一个参数,参数被当做上一条yield的返回值。
function* generator() {
const list = [1, 2, 3];
for (let i of list) {
yield i;
} }
let g = generator();
console.log(g.next()); // {value: 1, done: false}
console.log(g.next()); // {value: 2, done: false}
console.log(g.next()); // {value: 3, done: false}
console.log(g.next()); // {value: undefined, done: true}
function* foo(x){
var y = 2 * (yield(x + 1));
var z = yield (y / 3);
return (x + y + z);
}
var a = foo(5);
a.next() //{value: 6, done: false}
a.next() //{value: NaN, done: false}, 2* undefined / 3 = NaN
a.next() //{value: NaN, done: true}, 5 + NaN + undefined = NaN
var b = foo(5)
b.next() //{value: 6, done: false}
b.next(12) //{value: 8, done: false} 2 * 12 / 3 = 8
b.next(13) //{value: , done: true} 5 + 2 * 12 + 13 = 42
2、消息传递
除了暂停和继续执行外,生成器同时支持传值。
在函数的内容可以使用yield的关键词向外返回一个值,在next方法调用的时候可以拿到这个值
2.1、Demo1
function* fn() {
var a = yield 'hello';
yield;
console.log(a);
}
var iter = fn();
var res = iter.next();
console.log(res);
iter.next(2);
iter.next(); //2
解析如下:
1、可以看到,yield后面有一个字符串‘hello’,在第一次调用next时,暂停在这里且返回给了iter.next(),即res = {done:false,value:‘hello’}
2、由于暂停的地方是一个赋值语句,需要一个变量给a,于是第二个next()方法中传了一个参数2替换了yield,最后打印a得到了2。
2.2、demo2
function *foo(x) {
let y = 2 * (yield (x + 1))
let z = yield (y / 3)
return (x + y + z)
}
let it = foo(5)
console.log(it.next()) // => {value: 6, done: false}
console.log(it.next(12)) // => {value: 8, done: false}
console.log(it.next(13)) // => {value: 42, done: true}
分析如下:
- 首先 Generator 函数调用和普通函数不同,它会返回一个迭代器
- 当执行第一次 next 时,传参会被忽略,并且函数暂停在 yield (x + 1) 处,所以返回 5 + 1 = 6
- 当执行第二次 next 时,传入的参数12就会被当作上一个yield表达式的返回值,如果你不传参,yield 永远返回 undefined。此时 let y = 2 * 12,所以第二个 yield 等于 2 * 12 / 3 = 8
- 当执行第三次 next 时,传入的参数13就会被当作上一个yield表达式的返回值,所以 z = 13, x = 5, y = 24,相加等于 42
3、yield与next()的使用
function* foo(x) {
console.log('first');
var y = 2 * (yield (x + 1));
console.log('second');
var z = yield (y / 3);
console.log('third');
return (x + y + z);
}
var a = foo(5);
a.next() // first Object{value:6, done:false}
a.next() // second Object{value:NaN, done:false}
a.next() // third Object{value:NaN, done:true}
var b = foo(5);
b.next() // first { value:6, done:false }
b.next(12) // second { value:8, done:false }
b.next(13) // third { value:42, done:true }
-
首次调用next()方法表示开始执行generator函数
-
之后在执行时遇到yield表达式,就
暂停执行yield表达式之后的操作,但是yield表达式要执行,并且将yield表达式之后的值作为返回对象的value值
。比如说上面的generator函数,
第一次调用next(),yield表达式x+1会执行,但是2*(yield (x+1))并不会执行 -
再次调用next(val)方法,继续向下执行,同时val参数可作为yield返回对象的value值,继续向下执行,如果没有传则默认为NaN。如此往复,直到执行完毕。
注意:yield返回的对象中done表示执行状态,是否执行完毕
4、生成器函数封装如下
const fetchData = (arg) => new Promise(resolve => {
setTimeout(() => {
resolve('test data ' + arg)
}, 3000)
})
// generator 函数最终返回一个Promise对象
// generator 中遇到yield, 先执行其后操作,等执行完成后,执行完next()方法,才能继续向下执行
function* test() {
console.log('0')
let res = yield fetchData('1')
console.log('1', res)
res = yield fetchData(res)
console.log('2', res)
res = yield fetchData(res)
console.log('3', res)
res = yield fetchData(res)
console.log('4', res)
}
// 自动执行器run
const run = g => {
const next = val => {
const res = g.next(val)
if(res.done) return res.value
res.value.then(next)
}
next()
}
run(test())
输出结果如下
参考地址:https://blog.csdn.net/qinshengnan520/article/details/108287199