基本概念
理解
Generator
函数是ES 6提供的一种异步编程解决方案。从语法上,可以将Generator
函数理解为一个状态机,它封装了多个内部状态。执行Generator
函数会返回一个遍历器对象,也就是说Generator
函数除了是一个状态机,还是一个遍历器对象生成函数。返回的遍历器对象,可以依次遍历Generator
函数内部的每一个状态。
为什么使用
Generator
最大的好处就是函数可以被暂停执行并保持上下文,这个运行方式在处理那些需要暂停的任务时非常有用,而被维持的上下文是为了在将来对运行环境进行恢复。
语法
Generator函数
Generator
函数以function *
申明开头,并在需要暂停运行的地方添加yield
关键字
function* myGenerator(){ // A yield 'foo' // B}
执行上面的myGenerator
方法会创建一个Generator
对象,我们可以通过next
方法来控制函数执行。运行next
方法会执行myGenerator
函数中的代码,直到碰到下一个yield
表达式(执行完yield
才暂停),此时yield
后的表达式的值就被返回出去了,而且myGenerator
的执行就暂停了,当我们再次运行next
方法时,myGenerator
会在上次暂停的地方接着向下运行。
const g = myGenerator()const state01 = g.next() // {value: 'foo', done: false}const state02 = g.next() // {value: undefined, done: true}
yield
yield
是伴随着Generator
函数出现的,它允许我们返回多个值(多个状态)。然而我们只能在Generator
中才能使用它。如果我们尝试在回调函数中用yield
一个值,即使在Generator
函数内部声明的,也会抛出错误
yield*
yield*
是用来在一个Generator
函数内部调用另一个Generator
函数的
function* foo(){ yield 'foo'}function* bar(){ yield 'bar' yield* foo() yield 'bar again'}const b = bar()b.next() // {value: 'bar', done: false}b.next() // {value: 'foo', done: false}b.next() // {value: 'bar again', done: false}b.next() // {value: undefined, done: true}
遍历
由于Generator
方法会返回一个遍历器对象(可遍历对象),因此我们可以使用一个遍历方法,例如for-of
遍历该对象内部的所有状态(值)
for-of
遍历
// 接上一个的例子for (let e of bar()) { console.log(e)}/* 依次输出barfoobar again*/注意,b对应的遍历器对象已经遍历完毕,因此下面的例子只会输出undefinedfor (let e of b) { console.log(e)}// undefined
解构运算实现遍历
console.log([...bar()]) // ['bar', 'foo', 'bar again']
return
我们可以在Generator
函数中增加return
语句
function* myGenerator(){ yield 'foo' yield 'bar' return 'done'}var g = myGenerator()g.next() // {value: 'foo', done: false}g.next() // {value: 'bar', done: false}g.next() // {value: 'done', done: true}g.next() // {value: undefined, done: true}
但是如果使用for-of
或者解构运算来遍历遍历器对象,则return
后面的值将被忽略
for(let e of myGenerator()) { console.log(e)}// foo// barconsole.log([...myGenerator])// ['foo', 'bar']