美文网首页
ES6 Generator

ES6 Generator

作者: 我叫Aliya但是被占用了 | 来源:发表于2018-01-25 14:55 被阅读16次
    function* myGeneratorFn(num) { 
      console.info('you are in Generator function!');
      yield;
      num = num * 2;
      if (num > 10) return num;
      else yield num;
      console.info('you are geting out Generator function!');
    }
    
    image.png image.png

    Generator是一个遍历器对象生成函数,内部封装了多个状态的状态机。(我的理解是一个允许执行中断、可半路与外部交互的函数)

    1. 定义:function* functionName
    2. yield:执行到此处暂时中断
    3. next:从中断的位置继续执行,直到return或函数结束(结束了也能调);

    next

    • next方法的返回
    { 
      value: obj,   // yield后面跟的表达式,没有或没有return返回undefined
      done: true   // 遍历是否结束,即执行到return或函数结束
    }
    

    done返回true后表名遍历结束,再调用next永远返回{ value: undefined, done: true }

    • next方法可传参
    function* foo(x) {
      var y = 2 * (yield (x + 1)); <---(yield (x + 1)) 为 12----------
      var z = yield (y / 3);                                         ^
      return (x + y + z);                                            |
    }                                                                |
                                                                     |
    var a = foo(5);                                                  |
    a.next() // Object{value:6, done:false}                          |
    a.next() // Object{value:NaN, done:false}                        |
    a.next() // Object{value:NaN, done:true}                         |
                                                                     |
    var b = foo(5);                                                  |
    b.next() // { value:6, done:false }                              |
    b.next(12) // { value:8, done:false }  -------12----------------->
    b.next(13) // { value:42, done:true }
    

    第一次next执行到第一个yield(只执行 yield xxx 这一部分),以此类推
    参数为上一个(注意:是上一个,上一个!)yield语句的返回值,如代码中参数12的走向

    yield

    • 仅generator中使用;用于forEach之类的回调方法中会报错
    • 用于表达式中时要使用()
    let x = 'I am ' + (yield somevalue);
    let num = yield somevalue;  // 这样不用
    

    for ... of

    function *foo() {
      yield 1;
      yield 2;
      yield 3;
      yield 4;
      yield 5;
      return 6;
    }
    
    // for...of 循环
    for (let v of foo()) console.log(v);   // 1 2 3 4 5 
    
    // 扩展运算符
    [...foo()] // [1, 2, 3, 4, 5]
    
    // Array.from 方法
    Array.from(foo()) //  [1, 2, 3, 4, 5]
    
    // 解构赋值
    let [x, y, z, i, j] = foo();  // x=1 ... j=5  
    
    • 不需要使用next方法
    • 一旦 done==true,循环就会中止,且不包含return 的值
    • 处理的是同步操作

    Generator.prototype.throw()

    var gen = function* gen(){
      try {
        yield console.log('a');
        yield console.log('b');
      } catch (e) {
        console.log('内部捕获', e);
      }
      yield console.log('c');
      yield console.log('d');
      yield console.log('e');
    }
    
    var g = gen(); 
    try {
      g.next()             // a
      g.throw('my error')  // 内部捕获 undefined, c 
      g.next()             // d
      g.throw('my error2') // 外部捕获 my error2
    } catch (e) {
      console.log('外部捕获', e);
    } 
    
    • throw('my error') 被方法内try块catch到,且块内剩下的语句不再执行('my error'其实应该用new Error('a'))
    • throw会附带执行一次next方法:打印出了c

    这种函数体内捕获错误的机制,大大方便了对错误的处理。多个yield语句,可以只用一个try...catch代码块来捕获错误。如果使用回调函数的写法,想要捕获多个错误,就不得不为每个函数内部写一个错误处理语句,现在只在Generator函数内部写一次catch语句就可以了。

    Generator.prototype.return()

    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 }
    
    • g.return会中断generator执行,方法参数为返回对象value的值
    • 如果generator方法中有finally,return方法会推迟到finally代码块执行完再执行
    function* gen() {
      try {
        yield 1;
        yield 2;
        yield 3;
      } finally {
        console.info('finally~')     
        yield 4;
      }
    }
    
    var g = gen();
    g.next()        // { value: 1, done: false }
    g.return('foo') // finally~, { value: 4, done: false}
    g.next()        // { value: "foo", done: true}
    

    yield* 语句

    用来在一个 Generator 函数里面执行另一个 Generator 函数

    function* inner() {
      yield 'hello!';
    }
    // -------直接调用--------
    function* fn1() {
      yield 'open';
      inner();
      yield 'close';
    }
    var gen = fn1()
    gen.next()  // { value: "open", done: false }
    gen.next()  // { value: "close", done: true } 
    gen.next()  // { value: undefined, done: true }
    
    // -------使用yield  --------
    function* fn2() {
      yield 'open';
      yield  inner();
      yield 'close';
    }
    var gen = fn2()
    gen.next()  // { value: "open", done: false }
    gen.next()  // 返回一个遍历器对象
    gen.next()  // { value: "close", done: true }
    
    // -------使用yield*  --------
    function* fn3() {
      yield 'open';
      yield* inner();
      yield 'close';
    }
    var gen = fn3()
    gen.next()  // { value: "open", done: false }
    gen.next()  // { value: "hello", done: false }
    gen.next()  // { value: "close", done: true }
    

    如果yield*后面跟一个数组对象(如["a", "b", "c"]),就需要多调用几次(3次)next才能全拿到;如果是yield就会一次拿到一个数组。

    Generator与一般function的不同

    1. Generator函数永远返回一个遍历器,它定义在this上的属性在外面访问不到
    2. 不能跟new命令一起出现,因为new的本质上是个constructor

    让Generator函数返回一个正常的对象实例

    Generator与状态机

    Generator与协程

    异步操作的同步化表达

    相关文章

      网友评论

          本文标题:ES6 Generator

          本文链接:https://www.haomeiwen.com/subject/ivgraxtx.html