Generator生成器函数

1.Generator函数是什么?
是ES6提供的一种异步编程解决方案,使得书写异步代码更优雅。
2.有什么特点?怎么用?
特点:
(1)function关键字与函数名之间有一个*号。
(2)函数体内部使用yield表达式,定义不同的内部状态(yield在英语里的意思就是“产出”)。执行Generator函数生成一个遍历器对象,用该对象调用其next()方法来遍历函数内部的状态。

function* helloWorldGenerator() {
  yield 'hello';
  yield 'world';
  return 'ending';
}
const hw = helloWorldGenerator();

(3)调用 Generator 函数,返回一个遍历器对象,代表 Generator 函数的内部指针。以后,每次调用遍历器对象的next方法,就会返回一个有着value和done两个属性的对象。value属性表示当前的内部状态的值,是yield表达式后面那个表达式的值;done属性是一个布尔值,表示是否遍历结束
3.注意:
(1)第一次调用Generator函数,返回遍历器对象。这时候,函数内部是没有执行的,仅仅返回遍历器对象。当调用next()方法时,函数才从头部开始执行,直到遇到yield表达式,则交出函数执行权,函数停止执行。

function *fn() {
    console.log(1);
    yield 1;
}
const g = fn(); // 这时候,函数内部的log语句不执行。控制台不会打印1的。
g.next(); // 这时候,控制台输出1

(2)遍历器对象调用next()方法,会移动内部指针,指向内部不同状态。假设函数未执行完,当指针还未指向下一状态时,则该指针一直指向上一个状态的。
(3) 每次调用next方法,内部指针就从函数头部或上一次停下来的地方开始执行,直到遇到下一个yield表达式(或return语句)为止

function* g() {
        yield 1;
        console.log('throwing an exception');
        throw new Error('generator broke!');
        yield 2;
        yield 3;
    }

    function log(generator) {
        var v;
        console.log('starting generator');
        try {
            v = generator.next();
            console.log('第一次运行next方法', v);
        } catch (err) {
            console.log('捕捉错误', v);
        }
        try {
            v = generator.next(); // 这里generator函数已经执行完了,并且,这里的赋值操作是没有完成的,在执行next()方法时,报错了,所以没有执行完next,就执行了catch
            console.log('第二次运行next方法', v);
        } catch (err) {
            console.log('捕捉错误--', v); // 所以,这里v的值还是第一次执行的next时,所得到的值。{value: 1, done: false}
        }
        try {
            v = generator.next();
            console.log('第三次运行next方法', v);
        } catch (err) {
            console.log('捕捉错误', v);
        }
        console.log('caller done');
    }
    log(g());

4.next()方法参数:
yield表达式本身没有返回值,或者说总是返回undefined。next方法可以带一个参数,该参数就会被当作上一个yield表达式的返回值。

function *fn() {
    const y = yield 1;
    const result = y * 2;
    console.log(result);
}
const g = fn()
g.next();
g.next(4); // 控制台打印8

(5)由生成器函数执行返回的遍历器对象,是生成器函数的实例。该遍历器对象的原型是该生成器函数的prototype对象。

function* gg() {
        console.log(this);
        const keyArr = Object.keys(this);
        for (let key of keyArr) {
            yield [key, this[key]]
        }
    }
    console.log(gg().__proto__ === gg.prototype); // true

5.生成器函数的应用:
(1)为不能用for…of遍历的数据结构部署遍历器接口。ES6 规定,默认的 Iterator 接口部署在数据结构的Symbol.iterator属性,或者说,一个数据结构只要具有Symbol.iterator属性,就可以认为是“可遍历的”(iterable)。Symbol.iterator属性本身是一个函数,就是当前数据结构默认的遍历器生成函数。执行这个函数,就会返回一个遍历器,还就是注意下,这里的this,指向的是对象。

function* gg() {
        console.log(this);
        const keyArr = Object.keys(this);
        for (let key of keyArr) {
            yield [key, this[key]]
        }
    }
    const obj = {
        name: 'ha',
        sex: 'man',
        age: 18,
        id: 320320,
        [Symbol.iterator]: gg
    }
    for (let key of obj) {
        console.log(key);
    }

在这里插入图片描述
(2)异步编程应用
Ajax 是典型的异步操作,通过 Generator 函数部署 Ajax 操作,可以用同步的方式表达

function* main() {
  var result = yield request("http://some.url");
  var resp = JSON.parse(result);
    console.log(resp.value);
}

function request(url) {
  makeAjaxCall(url, function(response){
    it.next(response); // 这里将request("http://some.url")请求结果,赋值给了result,并且,最终函数执行完了。
  });
}

var it = main();
it.next();

上面代码的main函数,就是通过 Ajax 操作获取数据。可以看到,除了多了一个yield,它几乎与同步操作的写法(异步还是异步,这个是没有变得。只是写法上像同步而已)完全一样。注意,makeAjaxCall函数中的next方法,必须加上response参数,因为yield表达式,本身是没有值的,总是等于undefined。

自动执行器

/**
 * yield异步操作,被包装成promise对象
 * @param count
 * @returns {Promise<any>}
 */
let n = 0;
function readCount(count) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      n += count;
      resolve(n);
    }, 1000)
  })
}

/**
 * generator函数
 * @returns {IterableIterator<*>}
 */
function * gen() {
  let a = yield readCount(1);
  let b = yield readCount(1);
  return  yield readCount(1);
}

/**
 * 自动执行器
 */
function myCo(gen) {
  const g = gen(); // 遍历器对象
  let result = null;
  function next(data) {
    const { value, done } = g.next(data);  // 遍历器对象调用next方法开始执行
    if (!done){
      value.then((res) => {
        next(res);
      })
    } else {
      result = value;
      console.log(result);
    }
  }
  next();
}
myCo(gen);

  • 2
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值