(十) Generator函数 (生成器)

1. Generator函数的基本概念

Generator 函数是es6提供的一种异步编程的解决方案

目前为止学过的异步处理的手段:

  • 定时任务
  • promise

有如下特性:

  • 使用function声明, 在function关键字和函数名之间有一个*
  • 函数内部使用yield表达式
  • 调用Generator函数并不会执行, 而是会返回一个迭代器对象

利用返回的迭代器对象, 调用内部的next方法, 返回一个包含value和done的结果对象

function* generator() {
  yield 'hello'
  yield 'world'
  return 100
}

let gen = generator()
// console.log(gen[Symbol.iterator]() === gen)   true
console.log(gen.next());
console.log(gen.next());
console.log(gen.next());

2. yield 表达式

yield表达式只能写在生成器函数里面

yield表达式相当于一个暂停标识, 当迭代器对象调用next方法后

  • 遇到yield表达式, 将紧跟在yield后面的表达式的值作为value的值, 然后, 会在此处暂停, 等待下一个next的调用
  • 再调用next, 往下执行后面的代码, 如果又碰到yield, 重复上一步
  • 如果往后都没有yield, 就将return后表达式的值,作为最后的value的值, done为true
  • 如果最后也没有return, 最后value的值为undefined

关于有return 和 没有 return 注意观察 done的状态

function* generator() {
  yield 'hello'
  yield 'world'
  // return 100
}

let gen = generator()
console.log(gen.next());
console.log(gen.next());
console.log(gen.next());

function* generator() {
  yield 'hello'
  yield 'world'
  return 100
}

let gen = generator()
console.log(gen.next());
console.log(gen.next());
console.log(gen.next());

当然, 也可以在生成器函数中不使用yield,此时生成器函数就变成了单纯的暂缓执行函数

2.1 返回值

yield表达式没有返回值, 也可以说返回值是undefined

但是可以通过调用next方法时, 为next方法传递参数,该参数会作为上一个yield表达式的返回值

function* generator() {
  let flag = yield
  if (flag) { return 100 }
  return 200
}

let gen = generator()	
console.log(gen.next())		// {value: undefined, done: false}
console.log(gen.next())   // {value: 200, done: true}

上下对比

function* generator() {
  let flag = yield
  if (flag) { return 100 }
  return 200
}

let gen = generator()
console.log(gen.next())		// {value: undefined, done: false}
console.log(gen.next(true))  // {value: 100, done: true}

因此, 可以利用这一特性, 在Generator 函数运行的不同阶段,从外部向内部注入不同的值,从而调整函数行为

2.2 for...of循环

使用for...of 循环遍历生成器函数返回的迭代器对象, 会自动调用next方法

到此处感觉跟python中的yield比较像了

斐波那契数列

function* fibonacci() {
  let [a, b] = [0, 1]
  for (let i = 0; i < 10; i++) {
    // 注意看这里, 加了分号结尾的
    yield b;
    [a, b] = [b, a + b]
  }
}

for (let num of fibonacci()) {
  console.log(num);
}
// 正常输出结果

如果不加分号

function* fibonacci() {
  let [a, b] = [0, 1]
  for (let i = 0; i < 10; i++) {
    // 注意看这里, 加了分号结尾的
    yield b[a, b] = [b, a + b]
  }
}

for (let num of fibonacci()) {
  console.log(num);
}
// 输出一连串的  [1, 1]     疑惑

yield表达式后面记得加分号

3. 迭代器对象调用return

返回的迭代器对象有一个return()方法, 与在生成器函数内部使用return一致

在哪个next方法后面使用, 就相当于在哪个yield后面使用

可以接收一个参数, 作为value的值, 不传参value为undefined

内部有try..finally, 当使用return后, 会立即进入finally , 与一般使用一致

function* generaror() {
  // try {
  //   yield 1
  //   yield 2
  // } finally {
  // yield 3
  // yield 4
  // }

  yield 1
  yield 2
  yield 3
  yield 4
}

let gen = generaror()
console.log(gen.next());
console.log(gen.return(100));

4. yield* 表达式

yield 表达式可以让我们在一个生成器函数中调用另外一个生成器函数*

function* fn1() {
  yield 1
  yield 2
}
function* fn2() {
  yield 3
  yield 4
  yield* fn1()
}

for (let i of fn2()) {
  console.log(i);   // 3 4 1 2
}

5. this问题

6. 异步请求

本质是通过next传参

function request(url) {
  axios.get(url).then(res => {
    gen.next(res)
  })
}

function* generator() {
  // 获取name
  let res1 = yield request('./data1.json')
  console.log(res1);
  // 通过name获取age
  let res2 = yield request('./data2.json')
  console.log(res2);
}

let gen = generator()
gen.next()
posted @ 2021-08-02 22:43  只猫  阅读(272)  评论(0编辑  收藏  举报