含义
- Generator函数是ES6提供的一种异步解决方案
- Generator函数是个状态机,封装了多个内部状态(yield),还是一个遍历器对象生成函数(next)(yield生成状态,next进行状态的流转)
特征
function
关键字和函数名之间有个星号 *****- 函数体内部使用
yield
表达式,定义不同的内部状态(yield
在英语里的意思就是“产出”),yield
表达式是暂停标志
function* HelloGenerator () {
yield "Hello";
yield "world";
return "Generator";
}
let hwg = HelloGenerator();
console.log(hwg); // 如下图
console.log(hwg.next()); // {value: "Hello", done: false}
console.log(hwg.next()); // {value: "world", done: false}
console.log(hwg.next()); // {value: "Generator", done: true}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Uy2voaJn-1621158215950)(E:\我的坚果云\markdown\es6\images\image-20210407101544333.png)]
yield表达式
特点:
-
暂停标志
-
紧跟在
yield
后面的那个表达式的值,作为返回的对象的value
属性值 -
yield
表达式只能用在 Generator 函数里面,用在其他地方都会报错let yieldArr = [1, [[2, 3], 4], [5, 6]]; let yieldGen = function* (a) { a.forEach(function (item) { // 报错 // a.forEach(function* (item) { // 不报错,达不到效果,外层function* 失效 if (typeof item !== 'number') { yield* yieldGen(item); } else { yield item; } }); }; for (let f of yieldGen(yieldArr)){ console.log(f); }
更改如下:
let yieldArr = [1, [[2, 3], 4], [5, 6]]; let yieldGen = function* (a) { for(let i = 0; i < a.length; i++) { if (typeof a[i] !== 'number') { yield* yieldGen(a[i]); } else { yield a[i]; } }; }; for (let f of yieldGen(yieldArr)){ console.log(f); } // 1, 2, 3, 4, 5, 6
-
yield
表达式如果用在另一个表达式之中,必须放在圆括号里面function* yieldExp(){ // console.log("Hello" + yield); // SyntaxError // console.log("hello" + yield 123); // SyntaxError console.log("hello" + (yield)); // console.log("Hello" + (yield 123)); // }
-
yield
表达式用作函数参数或放在赋值表达式的右边,可以不加括号 -
yield
表达式本身没有返回值,或者说总是返回undefined
与Iterator接口的关系
- Generator 函数赋值给对象的
Symbol.iterator
属性,从而使得该对象具有 Iterator 接口
next方法的参数
-
next
方法可以带一个参数,该参数就会被当作上一个yield
表达式的返回值(yield
本身没有返回值,可以用next来决定它的返回值)function* nextArg() { for(var i = 0; true; i++) { var reset = yield i; if(reset) { i = -1; } } } var g = nextArg(); g.next() // { value: 0, done: false } g.next() // { value: 1, done: false } g.next(true) // { value: 0, done: false }
小试牛刀:
function* calNext(x) { var y = 2 * (yield (x + 1)); var z = yield (y / 3); return (x + y + z); } var a = calNext(5); a.next() // Object{value:6, done:false} a.next() // Object{value:NaN, done:false} a.next() // Object{value:NaN, done:true} var b = calNext(5); b.next() // { value:6, done:false } b.next(12) // { value:8, done:false } b.next(13) // { value:42, done:true }
课后作业:
- 实现第一次next参数注入能生效的效果
for…of…循环
-
一旦
next
方法的返回对象的done
属性为true
,for...of
循环就会中止,且不包含该返回对象// 小试牛刀: function* exForOf() { yield 1; yield 2; yield 3; yield 4; yield 5; return 6; } for (let v of exForOf()) { console.log(v); } // 输出可以在评论回复 (1,2,3,4,5,undefind)
-
for...of
、扩展运算符(...
)、解构赋值和Array.from
内部调用的都是遍历器接口
Generator.prototype.throw()
-
在函数体外抛出错误,然后在 Generator 函数体内捕获
var gThrow = function* () { try { yield; } catch (e) { console.log('内部捕获', e); } }; var i = gThrow(); i.next(); try { i.throw('a'); i.throw('b'); } catch (e) { console.log('外部捕获', e); } // 内部捕获 a // 外部捕获 b
-
throw
方法抛出的错误要被内部捕获,前提是必须至少执行过一次next
方法function* gen() { try { yield 1; } catch (e) { console.log('内部捕获'); } } var g = gen(); g.throw(1); // Uncaught 1 // 解释:因为第一次执行next方法,等同于启动执行 Generator 函数的内部代码,否则 Generator 函数还没有开始执行,这时throw方法抛错只可能抛出在函数外部。
-
throw
方法被捕获以后,会附带执行下一条yield
表达式。也就是说,会附带执行一次next
方法var gen = function* gen(){ try { yield console.log('a'); } catch (e) { // ... } yield console.log('b'); yield console.log('c'); } var g = gen(); g.next() // a g.throw() // b g.next() // c
-
throw
命令与g.throw
方法是无关的,两者互不影响
原文出自:https://es6.ruanyifeng.com/#docs/generator