前言
Generator 函数是 es6 提供的一种异步编程的解决方案, 语法行为与传统函数完全不一样.
Generator 函数有多种理解角度, 从语法上, 首先可以把它理解成, Generator 函数是一个状态机, 封装了多个内部状态.
执
行 Generator 函数会返回一个遍历器对象, 也就是说, Generator 函数除了是状态机还是一个遍历器对象生成函数. 返回遍历器对象, 可以依次遍历 Generator 函数内部的每一个状态.
下面话不多说了, 来一起看看详细的介绍吧
语法上
首先可以把它理解成, Generator 函数是一个状态机, 封装了多个内部状态. 执行 Generator 函数会返回一个遍历器对象, 也就是说, Generator 函数除了状态机, 还是一个遍历器对象生成函数. 返回的遍历器对象, 可以依次遍历 Generator 函数内部的每一个状态.
形式上
Generator 函数是一个普通函数, 但是有两个特征.
一是, function 关键字与函数名之间有一个星号;
二是, 函数体内部使用 yield 表达式, 定义不同的内部状态(yield 在英语里的意思就是 "产出").
调用上
Generator 函数的调用方法与普通函数一样, 也是在函数名后面加上一对圆括号. 不同的是, 调用 Generator 函数后, 该函数并不执行, 返回的也不是函数运行结果, 而是一个指向内部状态的指针对象, 也就是上一章介绍的遍历器对象 (Iterator Object). 我们必须调用遍历器对象的 next 方法, 使得指针移向下一个状态. 也就是说, 每次调用 next 方法, 内部指针就从函数头部或上一次停下来的地方开始执行, 直到遇到下一个 yield 表达式(或 return 语句) 为止. 换言之, Generator 函数是分段执行的, yield 表达式是暂停执行的标记, 而 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 函数, 返回一个遍历器对象, 代表 Generator 函数的内部指针. 以后, 每次调用遍历器对象的 next 方法, 就会返回一个有着 value 和 done 两个属性的对象. value 属性表示当前的内部状态的值, 是 yield 表达式后面那个表达式的值; done 属性是一个布尔值, 表示是否遍历结束.
yield 表达式
yield 表达式与 return 语句既有相似之处, 也有区别. 相似之处在于, 都能返回紧跟在语句后面的那个表达式的值. 区别在于每次遇到 yield, 函数暂停执行, 下一次再从该位置继续向后执行, 而 return 语句不具备位置记忆的功能. 一个函数里面, 只能执行一次(或者说一个)return 语句, 但是可以执行多次(或者说多个)yield 表达式. 正常函数只能返回一个值, 因为只能执行一次 return;Generator 函数可以返回一系列的值, 因为可以有任意多个 yield. 从另一个角度看, 也可以说 Generator 生成了一系列的值, 这也就是它的名称的来历(英语中, generator 这个词是 "生成器" 的意思).
语法注意点:
1.yield 表达式只能用在 Generator 函数里面
2.yield 表达式如果用在另一个表达式之中, 必须放在圆括号里面
3.yield 表达式用作函数参数或放在赋值表达式的右边, 可以不加括号.
例如:
- function* demo() {
- foo(yield 'a', yield 'b'); // OK
- let input = yield; // OK
- }
next 方法的参数
yield 表达式本身没有返回值(就是说 let a=yield ; 会返回 undefined), 或者说总是返回 undefined.next 方法可以带一个参数, 该参数就会被当作上一个 yield 表达式的返回值 (注意, 是表达式的返回值, 例如 let a=yield.......... 参数会是 a 的值并且会覆盖表达式之前的值).
- function* f() {
- for(var i = 0; true; i++) {
- var reset = yield i;
- console.log(reset);
- if(reset) { i = -1; }
- }
- }
- var g = f();
- g.next()
由于 next 方法的参数表示上一个 yield 表达式的返回值, 所以在第一次使用 next 方法时, 传递参数是无效的. V8 引擎直接忽略第一次使用 next 方法时的参数, 只有从第二次使用 next 方法开始, 参数才是有效的. 从语义上讲, 第一个 next 方法用来启动遍历器对象, 所以不用带有参数.
总结
来源: https://www.jb51.net/article/159185.htm