function* myGeneratorFn(num) {
console.info('you are in Generator function!');
yield;
num = num * 2;
if (num > 10) return num;
else yield num;
console.info('you are geting out Generator function!');
}
image.png
image.png
Generator是一个遍历器对象生成函数,内部封装了多个状态的状态机。(我的理解是一个允许执行中断、可半路与外部交互的函数)
- 定义:function* functionName
- yield:执行到此处暂时中断
- next:从中断的位置继续执行,直到return或函数结束(结束了也能调);
next
- next方法的返回
{
value: obj, // yield后面跟的表达式,没有或没有return返回undefined
done: true // 遍历是否结束,即执行到return或函数结束
}
done返回true后表名遍历结束,再调用next永远返回{ value: undefined, done: true }
- next方法可传参
function* foo(x) {
var y = 2 * (yield (x + 1)); <---(yield (x + 1)) 为 12----------
var z = yield (y / 3); ^
return (x + y + z); |
} |
|
var a = foo(5); |
a.next() // Object{value:6, done:false} |
a.next() // Object{value:NaN, done:false} |
a.next() // Object{value:NaN, done:true} |
|
var b = foo(5); |
b.next() // { value:6, done:false } |
b.next(12) // { value:8, done:false } -------12----------------->
b.next(13) // { value:42, done:true }
第一次next执行到第一个yield(只执行 yield xxx 这一部分),以此类推
参数为上一个(注意:是上一个,上一个!)yield语句的返回值,如代码中参数12的走向
yield
- 仅generator中使用;用于forEach之类的回调方法中会报错
- 用于表达式中时要使用()
let x = 'I am ' + (yield somevalue);
let num = yield somevalue; // 这样不用
for ... of
function *foo() {
yield 1;
yield 2;
yield 3;
yield 4;
yield 5;
return 6;
}
// for...of 循环
for (let v of foo()) console.log(v); // 1 2 3 4 5
// 扩展运算符
[...foo()] // [1, 2, 3, 4, 5]
// Array.from 方法
Array.from(foo()) // [1, 2, 3, 4, 5]
// 解构赋值
let [x, y, z, i, j] = foo(); // x=1 ... j=5
- 不需要使用next方法
- 一旦 done==true,循环就会中止,且不包含return 的值
- 处理的是同步操作
Generator.prototype.throw()
var gen = function* gen(){
try {
yield console.log('a');
yield console.log('b');
} catch (e) {
console.log('内部捕获', e);
}
yield console.log('c');
yield console.log('d');
yield console.log('e');
}
var g = gen();
try {
g.next() // a
g.throw('my error') // 内部捕获 undefined, c
g.next() // d
g.throw('my error2') // 外部捕获 my error2
} catch (e) {
console.log('外部捕获', e);
}
- throw('my error') 被方法内try块catch到,且块内剩下的语句不再执行('my error'其实应该用new Error('a'))
- throw会附带执行一次next方法:打印出了c
这种函数体内捕获错误的机制,大大方便了对错误的处理。多个yield语句,可以只用一个try...catch代码块来捕获错误。如果使用回调函数的写法,想要捕获多个错误,就不得不为每个函数内部写一个错误处理语句,现在只在Generator函数内部写一次catch语句就可以了。
Generator.prototype.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 }
- g.return会中断generator执行,方法参数为返回对象value的值
- 如果generator方法中有finally,return方法会推迟到finally代码块执行完再执行
function* gen() {
try {
yield 1;
yield 2;
yield 3;
} finally {
console.info('finally~')
yield 4;
}
}
var g = gen();
g.next() // { value: 1, done: false }
g.return('foo') // finally~, { value: 4, done: false}
g.next() // { value: "foo", done: true}
yield* 语句
用来在一个 Generator 函数里面执行另一个 Generator 函数
function* inner() {
yield 'hello!';
}
// -------直接调用--------
function* fn1() {
yield 'open';
inner();
yield 'close';
}
var gen = fn1()
gen.next() // { value: "open", done: false }
gen.next() // { value: "close", done: true }
gen.next() // { value: undefined, done: true }
// -------使用yield --------
function* fn2() {
yield 'open';
yield inner();
yield 'close';
}
var gen = fn2()
gen.next() // { value: "open", done: false }
gen.next() // 返回一个遍历器对象
gen.next() // { value: "close", done: true }
// -------使用yield* --------
function* fn3() {
yield 'open';
yield* inner();
yield 'close';
}
var gen = fn3()
gen.next() // { value: "open", done: false }
gen.next() // { value: "hello", done: false }
gen.next() // { value: "close", done: true }
如果yield*后面跟一个数组对象(如["a", "b", "c"]),就需要多调用几次(3次)next才能全拿到;如果是yield就会一次拿到一个数组。
Generator与一般function的不同
- Generator函数永远返回一个遍历器,它定义在this上的属性在外面访问不到
- 不能跟new命令一起出现,因为new的本质上是个constructor
网友评论