ES6之Generator函数(function*)

含义

  1. Generator函数是ES6提供的一种异步解决方案
  2. Generator函数是个状态机,封装了多个内部状态(yield),还是一个遍历器对象生成函数(next)(yield生成状态,next进行状态的流转)

特征

  1. function关键字和函数名之间有个星号 *****
  2. 函数体内部使用yield表达式,定义不同的内部状态(yield在英语里的意思就是“产出”),yield表达式是暂停标志
function* HelloGenerator () {
    yield "Hello";
    yield "world";
    return "Generator";
}
let hwg = HelloGenerator();
console.log(hwg);         // 如下图
console.log(hwg.next());  // {value: "Hello", done: false}
console.log(hwg.next());  // {value: "world", done: false}
console.log(hwg.next());  // {value: "Generator", done: true}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Uy2voaJn-1621158215950)(E:\我的坚果云\markdown\es6\images\image-20210407101544333.png)]

yield表达式

特点:

  1. 暂停标志

  2. 紧跟在yield后面的那个表达式的值,作为返回的对象的value属性值

  3. yield表达式只能用在 Generator 函数里面,用在其他地方都会报错

    let yieldArr = [1, [[2, 3], 4], [5, 6]];
    let yieldGen = function* (a) {
        a.forEach(function (item) { // 报错 
        // a.forEach(function* (item) { // 不报错,达不到效果,外层function* 失效
            if (typeof item !== 'number') {
                yield* yieldGen(item);
            } else {
                yield item;
            }
        });
    };
    for (let f of yieldGen(yieldArr)){
        console.log(f);
    }
    

    更改如下:

    let yieldArr = [1, [[2, 3], 4], [5, 6]];
    let yieldGen = function* (a) {
        for(let i = 0; i < a.length; i++) {
            if (typeof a[i] !== 'number') {
                yield* yieldGen(a[i]);
            } else {
                yield a[i];
            }
        };
    };
    for (let f of yieldGen(yieldArr)){
        console.log(f);
    }
    // 1, 2, 3, 4, 5, 6
    
  4. yield表达式如果用在另一个表达式之中,必须放在圆括号里面

    function* yieldExp(){
        // console.log("Hello" + yield);     // SyntaxError
        // console.log("hello" + yield 123); // SyntaxError
        console.log("hello" + (yield));      // 
        console.log("Hello" + (yield 123));  //
    }
    
  5. yield表达式用作函数参数或放在赋值表达式的右边,可以不加括号

  6. yield表达式本身没有返回值,或者说总是返回undefined

与Iterator接口的关系

  1. Generator 函数赋值给对象的Symbol.iterator属性,从而使得该对象具有 Iterator 接口

next方法的参数

  1. next方法可以带一个参数,该参数就会被当作上一个yield表达式的返回值(yield本身没有返回值,可以用next来决定它的返回值)

    function* nextArg() {
      for(var i = 0; true; i++) {
        var reset = yield i;
        if(reset) { i = -1; }
      }
    }
    var g = nextArg();
    g.next() 		// { value: 0, done: false }
    g.next() 		// { value: 1, done: false }
    g.next(true) 	// { value: 0, done: false }
    

    小试牛刀:

    function* calNext(x) {
      var y = 2 * (yield (x + 1));
      var z = yield (y / 3);
      return (x + y + z);
    }
    var a = calNext(5);
    a.next() 			// Object{value:6, done:false}
    a.next() 			// Object{value:NaN, done:false}
    a.next() 			// Object{value:NaN, done:true}
    
    var b = calNext(5);
    b.next() 			// { value:6, done:false }
    b.next(12) 			// { value:8, done:false }
    b.next(13) 			// { value:42, done:true }
    

    课后作业:

    1. 实现第一次next参数注入能生效的效果

for…of…循环

  1. 一旦next方法的返回对象的done属性为truefor...of循环就会中止,且不包含该返回对象

    // 小试牛刀:
    function* exForOf() {
      yield 1;
      yield 2;
      yield 3;
      yield 4;
      yield 5;
      return 6;
    }
    
    for (let v of exForOf()) {
      console.log(v);
    }
    // 输出可以在评论回复  (1,2,3,4,5,undefind)
    
  2. for...of、扩展运算符(...)、解构赋值和Array.from内部调用的都是遍历器接口

Generator.prototype.throw()

  1. 在函数体外抛出错误,然后在 Generator 函数体内捕获

    var gThrow = function* () {
      try {
        yield;
      } catch (e) {
        console.log('内部捕获', e);
      }
    };
    
    var i = gThrow();
    i.next();
    
    try {
      i.throw('a');
      i.throw('b');
    } catch (e) {
      console.log('外部捕获', e);
    }
    // 内部捕获 a
    // 外部捕获 b
    
  2. throw方法抛出的错误要被内部捕获,前提是必须至少执行过一次next方法

    function* gen() {
      try {
        yield 1;
      } catch (e) {
        console.log('内部捕获');
      }
    }
    
    var g = gen();
    g.throw(1);
    // Uncaught 1
    // 解释:因为第一次执行next方法,等同于启动执行 Generator 函数的内部代码,否则 Generator 函数还没有开始执行,这时throw方法抛错只可能抛出在函数外部。
    
  3. throw方法被捕获以后,会附带执行下一条yield表达式。也就是说,会附带执行一次next方法

    var gen = function* gen(){
      try {
        yield console.log('a');
      } catch (e) {
        // ...
      }
      yield console.log('b');
      yield console.log('c');
    }
    
    var g = gen();
    g.next() 		// a
    g.throw() 		// b
    g.next() 		// c
    
  4. throw命令与g.throw方法是无关的,两者互不影响

原文出自:https://es6.ruanyifeng.com/#docs/generator

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值