Generator
Generator 是 ES6 提出的一种异步编程的解决办法,它与传统的函数完全不同,本章从基础概念和基本用法进行讲解和解析。在此之前也是对 Generator 函数云里雾里,所以通过此次学习,希望能对 Generator 有更深的理解和认识。
1.概念
Generator 中文的意思是’生成器’,阮一峰:ECMAScript 6 入门中对 Generator 解释是:
Generator 函数有多种理解角度。语法上,首先可以把它理解成,Generator 函数是一个状态机,封装了多个内部状态。
执行 Generator 函数会返回一个遍历器对象,也就是说,Generator 函数除了状态机,还是一个遍历器对象生成函数。返回的遍历器对象,可以依次遍历 Generator 函数内部的每一个状态。
Generator 有两个特点:
第一个是在定义时,要在 function 关键字和函数名中加一个星号,第二个就是在函数体中运用 yeild 表达式表示不同的状态。
1 | function* foo() { |
和普通函数不同的是,当运行这个函数时,返回不是这个函数的结果,而是一个遍历器对象,或者可以说是一个含有内部状态指针的对象。
如果想输出值得话,要用 next 方法来进行输出。next 方法就是向下移动指针,即每次调用 next 方法,就是从函数头部或者从上一次 yield 表达式移动到下一次 yield 表达式(或者 return 为止)。
1 | function* foo() { |
上述可以看出,调用 next 时输出的是一个对象,即 value 值代表 yield 后面的结果,done 代表遍历还没有结束,当遍历结束时,value 值都为 undefined,done 都为 true。
2.yield 表达式
yield 有个懒惰的特性,即 yield 后面的表达式,如果不调用 next 方法,是不会执行的。
1 | function* foo() { |
只有当 next 指针移动到该 yield 的时候,才会执行后面的表达式。
yield 和 return 是有不同之处的,在 Generator 函数中,可以定义多个 yield 表达式,但是 return 只能定义一个,并且在 yield 中遍历还没有完成,但在遇到 return 时,遍历就终止了。
1 | function* foo() { |
当遇到 return 时,遍历结束 done 为 true,value 值为 return 后的结果,在此之后的 next 的结果都为{value: undefined, done: true}。
如果在 Generator 函数中没有 yield 表达式:
1 | function* foo() { |
foo()返回的依旧是一个含有内部状态指针的对象,只有当 next 方法执行时该函数才会执行。
当 yield 和其他表达式融合时,如果 yield 表达式在左边,要将 yield 表达式用圆括号包裹,否则就会报语法错误。
1 | function* foo() { |
当用作函数参数或放在赋值表达式的右边,可以不加括号。
1 | function* demo() { |
3.next 方法
yield 是没有返回值的,它的返回值是 undefined,我们可以通过 next 方法将参数传递给 yield,此参数将会为 yield 的返回值。
1 | function* f() { |
上面代码先定义了一个可以无限运行的 Generator 函数 f,如果 next 方法没有参数,每次运行到 yield 表达式,变量 reset 的值总是 undefined。当 next 方法带一个参数 true 时,变量 reset 就被重置为这个参数(即 true),因此 i 会等于-1,下一轮循环就会从-1 开始递增。
不用 next 方法是否可以输出值呢?答案是可以的,可以用 for…of…方法遍历 Generator 函数。
1 | function* foo() { |
值得注意的是,for…of…在 done 为 ture 的时候就会终止执行,所以 return 后的 6 没有输出。
4.yield* 表达式
yield* 表达式是为了解决在一个 Generator 函数中调用另一个 Generator 函数所提供的方法。
1 | function* foo() { |
当 yield*后边的 Generator 函数中没有 return 时,作用就是 for…of…遍历 Generator 函数,如果 Generator 函数中有 return 时,则获取的是 return 值。
1 | function* foo() { |
其实 yield*后面只要是带有 Iterator 接口的都会被遍历。
1 | function* foo() { |
再举个例子:
1 | function* foo() { |
上述中 foo 是拥有 return 表达式的函数,所以 return 后的结果会‘赋值’给 yield*表达式的返回值,所以 result 是’END’,并且拓展运算符默认调用 Iterator 接口,所以会先打印出 result,然后再执行 yield。