Generator函数

本系列属于阮一峰老师所著的ECMAScript 6 入门学习笔记javascript


概念

Generator函数是ES6提供的一种异步编程解决方案。形式上,Generator函数是一个普通函数,可是有两个特征:function关键字与函数名之间有一个星号;函数体内部使用yield表达式,定义不一样的内部状态(yield表意产出)。java

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表达式就是暂停函数执行的暂停标志。es6

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
相关文章
相关标签/搜索