Generator函数
本系列属于阮一峰老师所著的ECMAScript 6 入门学习笔记
概念
Generator函数是ES6提供的一种异步编程解决方案。形式上,Generator函数是一个普通函数,但是有两个特征:function关键字与函数名之间有一个星号;函数体内部使用yield表达式,定义不同的内部状态(yield表意产出)。
function* helloWorldGenerator(){
yield 'hello'
yield 'world'
return 'ending'
}
var hw = helloWorldGenerator()
// Generator函数和普通函数一样调用,但是调用后函数并不执行,返回一个指向内部状态的指针对象,即遍历器对象(Iterator Object)
hw.next() // {value: 'hello',done:false}
hw.next() // {value: 'world',done:false}
hw.next() // {value: 'ending',done:true}
hw.next() // {value: undefined,done:true}
// 每次调用next方法,执行Generator函数,依次返回yeild表达式的值,执行到return语句(若没有return语句就执行到结束)
yield表达式
Generator函数返回遍历器对象,只有调用next方法才会遍历下一个状态,yield表达式就是暂停函数执行的暂停标志。
Generator函数可以不用yield表达式,这时就变成一个单纯的暂缓执行函数
function* f(){
console.log('done!')
}
var generator = f() // 函数不会立即执行,只有调用next方法才会执行
setTimeout(function(){
generator.next()
},2000)
yield表达式如果在另一个表达式中,必须放在圆括号内
function* demo(){
console.log('Hello' + yield) // SyntaxError
console.log('Hello' + (yield)) // OK
}
// yield表达式用作函数参数或放在赋值表达式的右边,可以不加括号
function* demo(){
foo(yield 'a',yield 'b') // OK
let input = yield // OK
}
与Iterator接口的关系
任何一个对象的Symbol.iterator方法,等于该对象的遍历器生成函数,调用该函数会返回该对象的一个遍历器对象。由于Generator函数就是遍历器生成函数,因此可以把Generator函数赋值给Symbol.iterator属性,从而使得该对象具有iterator接口。
var myIterator = {}
myIterator[Symbol.iterator] = function* (){
yield 1
yield 2
yield 3
}
[...myIterator] // [1,2,3]
next方法的参数
yield表达式本身没有返回值,或者说每次都返回undefined。next方法可以带一个参数,该参数会被当做上一个yield表达式的返回值
function* f() {
for(var i = 0; true; i++) {
var reset = yield i;
if(reset) { i = -1; }
}
}
var g = f();
g.next() // { value: 0, done: false }
g.next() // { value: 1, done: false }
g.next(true) // { value: 0, done: false }
// 每次运行到yield表达式,变量reset总被赋值undefined,当next带上参数之后,遍历reset就被赋值为true
for...of循环
for...of循环可以自动遍历Generator函数生成的Iterator对象,且不用再调用next方法
function* foo(){
yield 1
yield 2
yield 3
yield 4
return 5
}
for(let v of foo()){
console.log(v)
}
// 1 2 3 4
// for...of循环遇到next方法返回对象的done为true就会中止,所以return返回的5不在循环之中
// 除了for...of循环外,拓展运算符...、解构赋值和Array.from都可以将Generator函数返回的Iterator对象作为参数
function* numbers () {
yield 1
yield 2
return 3
yield 4
}
// 拓展运算符
[...numbers()] // [1,2]
// Array.from
Array.from(numbers()) // [1,2]
// 解构赋值
let [x,y] = numbers()
x // 1
y // 2
// for...of循环
for(let n of numbers()){
console.log(n) // 1 2
}
Generator.prototype.throw()
Generator函数返回的遍历器对象有一个throw方法,可以在函数体外抛出错误,然后在函数体内捕获
var g = function* (){
try{
yield
}catch(e){
console.log('内部捕获',e)
}
}
var i = g()
i.next()
// throw方法可以接收一个参数,改参数会被catch语句接收,建议抛出Error对象实例
try {
i.throw('a')
i.throw('b')
}catch(e){
console.log('外部捕获',e)
}
// 内部捕获 a
// 外部捕获 b
// throw方法被捕获后会附带执行下一条yield表达式,也就是说会附带执行一次next方法
Generator.prototype.return()
return方法返回给定的值,并且终结遍历Generator函数
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}
// 如果Generator函数内部有try...finally代码块,那么return方法会推迟到finally代码执行完之后再执行
function* numbers(){
yield 1
try{
yield 2
yield 3
}finally{
yield 4
yield 5
}
yield 6
}
var g = numbers()
g.next() // {value:1,done:false}
g.next() // {value:2,done:false}
g.return(7) // {value:4,done:false}
g.next() // {value:5,done:false}
g.next() // {value:7,done:true}
yield* 表达式
在Generator函数内调用另一个Generator函数,默认情况下是没有效果的,这时候就要用到yield*表达式
function* inner(){
yield 'hello!'
}
function* outer1(){
yield 'open'
yield inner()
yield 'close'
}
var gen = outer1()
gen.next().value // 'open'
gen.next().value // 返回一个遍历器对象
gen.next().value // 'close'
function* outer2(){
yield 'open'
yield* inner()
yield 'close'
}
var gen = outer2()
gen.next().value // 'open'
gen.next().value // 'hello'
gen.next().value // ’close'
function* concat(iter1,iter2){
yield* iter1
yield* iter2
}
// 等同于
function* concat(iter1,iter2){
for(var value of iter1){
yield value
}
for(var value of iter2){
yield value
}
}
function* gen(){
yield* ['a','b','c']
}
gen().next() // {value:'a',done:false}
// 若不加星号返回的是整个数组,加了就表示返回的是数组的遍历器对象。实际上任何具有Iterator接口的数据结构都可以被yield遍历
function *foo() {
yield 2;
yield 3;
return "foo";
}
function *bar() {
yield 1;
var v = yield *foo();
console.log( "v: " + v );
yield 4;
}
var it = bar();
it.next()
// {value: 1, done: false}
it.next()
// {value: 2, done: false}
it.next()
// {value: 3, done: false}
it.next();
// "v: foo"
// {value: 4, done: false}
it.next()
// {value: undefined, done: true}
//被代理的Generator函数foo有return语句,那么就会向代理它的Generator函数bar返回数据,并且继续执行next方法
作为对象属性的Generator函数
let obj = {
* myGeneratorMethod(){
...
}
}
Generator函数的this
// 生成空对象,使用call方法绑定Generator函数内部的this
function* F(){
this.a = 1
yield this.b = 2
yield this.c = 3
}
var obj = {}
var f = F.call(obj)
// 调用三次next方法完成F内部所有代码的运行,将所有内部属性绑定在obj对象上
f.next() // {value:2,done:false}
f.next() // {value:3,done:false}
f.next() // {value:undefined,done:true}
// obj对象编程了F的实例
obj.a // 1
obj.b // 2
obj.c // 3
// 将F改成构造函数,可以执行new命令
function* gen(){
this.a = 1
yield this.b = 2
yield this.c = 3
}
function F(){
return gen.call(gen.prototype)
}
var f = new F()
f.next() // {value:2,done:false}
f.next() // {value:3,done:false}
f.next() // {value:undefined,done:true}
f.a // 1
f.b // 2
f.c // 3

浙公网安备 33010602011771号