Generator的概念
Generator是ES6提供的异步编程解决方案,语法行为与传统的函数不同,可以将它理解成一个状态机,封装了多个内部状态。
形式上,Generator 函数是一个普通函数,但是有两个特征:
function
关键字与函数名之间有一个星号;- 函数体内部使用
yield
表达式,定义不同的内部状态(yield
在英语里的意思就是“产出”)。
function* helloWorldGenerator() { yield 'hello'; yield 'world'; return 'ending'; } var hw = helloWorldGenerator();
调用 Generator 函数后,该函数并不执行,返回的也不是函数运行结果,而是一个指向内部状态的指针对象,就是遍历器对象。
必须调用next
方法,使得指针移到下一个状态,每次调用next
方法,内部指针从函数头部或者上一次停止的地方开始执行,直到遇到yield
表达式,yield
后面的表达式的结果作为本次执行的value
,并且返回迭代是否完成的状态done
;如果遇到return
就将return
后面表达式的值作为返回值,同时done=true
,如果函数没有return
,value
就是undefined。
如果Generator函数执行完毕,再次调用next
,返回结果为{ value: undefined, done: true }
hw.next(); // { value: 'hello', done: false } hw.next(); // { value: 'world', done: false } hw.next(); // { value: 'ending', done: true } hw.next(); // { value: undefined, done: true }
与Iterator的关系
Generator
函数会返回一个迭代器对象,该对象具有Symbol.iterator
属性,执行后返回自身。
function* gen(){ // some code } var g = gen(); g[Symbol.iterator]() === g // true
因此可以把Generator
函数设置给对象的Symbol.iterator
属性,从而使该对象具有iterator接口,
var myIterable = {}; myIterable[Symbol.iterator] = function* () { yield 1; yield 2; yield 3; }; [...myIterable] // [1, 2, 3]
for...of循环
for...of可以自动遍历Generator函数运行时生成的对象,所以不需要手动调用next
方法。注意一旦next
返回对象的done
为true
,for...of循环就会停止,并且不包含该返回对象。所以下面的例子中6
没有被打印出来。
function* foo() { yield 1; yield 2; yield 3; yield 4; yield 5; return 6; } for (let v of foo()) { console.log(v); } // 1 2 3 4 5
同时for...of可以遍历具有Symbol.iterator
属性的对象。
var myIterable = { [Symbol.iterator]: function* () { yield 1; yield 2; yield 3; } }; for (let value of myIterable) { console.log(value);// 1 2 3 }
Generator函数嵌套使用
ES6提供了yield*
表达式,用来在一个 Generator 函数里面执行另一个 Generator 函数。
function* foo() { yield 'a'; yield 'b'; } function* bar() { yield 'x'; yield* foo(); yield 'y'; } // 等同于 function* bar() { yield 'x'; yield 'a'; yield 'b'; yield 'y'; } // 等同于 function* bar() { yield 'x'; for (let v of foo()) { yield v; } yield 'y'; } for (let v of bar()){ console.log(v); }