zoukankan      html  css  js  c++  java
  • ES6

    Generator函数

    1.Generator函数是ES6增加的异步编程解决方案之一,与普通的函数行为完全不同,类似于一个状态机,内部封装了多个状态。

    在函数定义的形式上,跟普通函数差不多,有两处不同,一是function关键字与函数名之间需要一个星号(*),二是函数内部使用yield语句定义各种状态,且yield只能用在Generator函数中,否则报错,如下所示

    function* testGenerator(){//星号只要在function与函数名之间就可
        yield 'test';
        yield 'generator';
        return '!';
    }
    
    function testYield(){
        yield 'hello';//报错,但在ff浏览器中不会报错,被自动认为是Generator函数
    }
    VM1211:9 Uncaught SyntaxError: Unexpected string(…)

    调用Generator函数,该函数不会立即执行,而是返回一个遍历器Iterator,必须调用该遍历器的next方法去遍历函数内部的下一个状态,如下所示

    function* generator(){
        console.log('hehe');
        yield 'hello';
        yield 'ecmascript';
        return 'end';
    }
    var gen = generator();
    gen.next();
    hehe
    Object { value: "hello", done: false }
    gen.next()
    Object { value: "ecmascript", done: false }
    gen.next()
    Object { value: "end", done: true }
    gen.next()
    Object { value: undefined, done: true }

    当然也可以使用for..of或者扩展运算符遍历,但不会遍历到return返回值,如下所示

    for(let x of generator()){
        console.log(x);
    }
    hehe
    hello
    ecmascript
    [...generator()].forEach((val,idx,arr)=>console.log(val));
    hehe
    hello
    ecmascript

    Generator函数中yield语句是暂停标志,可以不存在该语句,这时函数可以当作是暂缓执行函数,如下所示

    function* genfunc(){
        console.log("稍后执行...");
    }
    
    var g = genfunc();
    
    setTimeout(() => g.next(),2000);
    3
    稍后执行...

     由于yield语句只能用在Generator函数中,因此在使用回调函数时需要特别的注意,比如在Generator函数使用数组的map,forEach方法时,不能在函数参数里面写yield语句,如下所示

    function* arrGene(arr){
        arr.forEach(function(val,idx,arr){
            yield val;
        });
    }
    console.log([...arrGene([1,2,3])]);
    VM142:4 Uncaught SyntaxError: Unexpected identifier(…)
    -------------------------使用for循环代替--------------------------
    function* arrGene(arr){
        for(let i=0,len=arr.length; i<len; i++){
            yield arr[i];
        }
    }
    console.log([...arrGene([1,2,3])]);
    VM179:8 [1, 2, 3]

    Generator是一个遍历器生成器,因此可以赋值给没有默认遍历器的对象的Symbol.iterator属性,让该对象能够使用for...of语句,如下所示

    var obj = {};
    obj[Symbol.iterator] = function* (){
        yield 'hello';
        yield 'world';
        return '!';
    }
    for(let x of obj){
        console.log(x);
    }
    hello
    world

    2.next方法参数

    Generator实例的next方法可以传递参数,作为该实例内部上一个yield语句的返回值,如不通过next方法传值,yield语句的返回值总是undefined,如下所示

    //不传值的情况
    function* generator(){
        console.log('hello generator...');
        let v = yield 'ni';
        let u = yield v+'test';
        return u+v+'';
    }
    var f = generator()
    f.next()
    hello generator...
    Object { value: "ni", done: false }
    f.next()
    Object { value: "undefinedtest", done: false }
    f.next()
    Object { value: "NaN", done: true }
    //传值的情况
    var z = generator();
    z.next();
    hello generator...
    Object { value: "ni", done: false }
    z.next('frist');
    Object { value: "fristtest", done: false }
    z.next('second');
    Object { value: "secondfrist", done: true }

    因此我们利用这一特性来向generator函数内部注入值来控制函数的执行,如下所示

    unction* gene(){
        console.log('start generating...');
        let ret = yield 'hello';
        if(ret == 'a'){
            yield 'a';
        }else{
            yield 'b';
        }
        return 'ending';
    }
    var g = gene();
    g.next()
    start generating...
    Object { value: "hello", done: false }
    g.next('c');
    Object { value: "b", done: false }
    g.next();
    Object { value: "ending", done: true }

    3.Generator实例方法throw

    throw方法可以在函数体外抛出错误,然后在generator函数内部捕获错误,但同时只能一条错误异常,如下所示

    function* catchGene(){
        try{
            yield 'try';
        }catch(e){
            console.log('generator函数内部捕获:'+e);
        }
    }
    var g = catchGene();
    try{
        console.log(g.next());
        g.throw('a');
        g.throw('b');
    }catch(e){
        console.log('全局捕获:'+e);
    }
    Object { value: "try", done: false }
    generator函数内部捕获:a
    全局捕获:b

    如果在generator函数体内没有部署try...catch语句,则generator实例throw抛出的错误不能被捕获,可以被全局catch捕获,如下所示

    function* gen(){
        yield 'hello';
        yield 'world';
    }
    var g = gen();
    try{
        g.throw('a');
    }catch(e){
        console.log('全局捕获:'+e);
    }
    全局捕获:a

    不管是generator实例throw方法或者throw命令抛出的错误,只要被捕获了就不会影响generator函数的next方法的执行,否则遍历直接终止,如下所示

    function* gen(){
        yield 'hello';
        yield 'world';
    }
    var g = gen();
    console.log(g.next());
    g.throw();
    console.log(g.next());
    VM226:7 Object {value: "hello", done: false}
    VM226:8 Uncaught undefined
    -----------------------使用try...catch捕获-----------------
    function* gen(){
        try{
            yield 'hello';
            
        }catch(e){
            console.log(e);
        }
        yield 'world';
        yield 'ending';
    }
    var g = gen();
    console.log(g.next());
    console.log(g.throw('a'));
    console.log(g.next());
    Object { value: "hello", done: false }
    a
    Object { value: "world", done: false }
    Object { value: "ending", done: false }

    特别注意的是catch捕获到错误后,继续执行到下一个yield语句,相当于再执行了一个next方法。

    generator函数内部抛出的错误,可以被函数体外的catch捕获,这时由于报错,JS引擎认为generator函数遍历完毕,之后再调用next都是返回{value:undefined,done:true}对象,如下所示

    function* gen(){
        yield 'hello';
        yield x+y;
        yield 'world';
    }
    var g = gen();
    console.log(g.next());
    try{
        console.log(g.next());
    }catch(e){
        console.log(e);
    }
    console.log(g.next());
    Object { value: "hello", done: false }
    ReferenceError: x is not defined
    堆栈跟踪:
    gen@debugger eval code:3:2
    @debugger eval code:9:14
    
    Object { value: undefined, done: true }

    4.Generator实例方法return

    该方法会返回给定的值,并终止generator函数的遍历,如下所示

    function* gen(){
        yield 'hello';
        yield 'world';
    }
    var g = gen();
    g.next()
    Object { value: "hello", done: false }
    g.return("return");
    Object { value: "return", done: true }
    g.next()
    Object { value: undefined, done: true }

    如果return方法没有给出任何值,则返回undefined,如果generator函数体内部部署了try...finally语句,return语句会被推迟到finally执行完后执行,如下所示

    function* gen(){
        try{
            yield 'hello';
        }finally{
            yield 'world';
        }
    }
    var g = gen();
    g.next()
    Object { value: "hello", done: false }
    g.return("nihao")
    Object { value: "world", done: false }
    g.next()
    Object { value: "nihao", done: true }
    g.next()
    Object { value: undefined, done: true }

    5.yield*语句

    yield*语句用在generator函数内部执行另一个遍历器对象,如下所示

    function* letter(){
        yield 'b';
        yield 'c';
        yield 'd';
    }
    function* gen(){
        yield "a";
        letter(); //直接调用没有效果
        yield "e";
    }
    [...gen()]
    Array [ "a", "e" ]
    ------------------------------------------
    function* letter(){
        yield 'b';
        yield 'c';
        yield 'd';
    }
    function* gen(){
        yield "a";
        yield* letter();//yield* 语句
        yield "e";
    }
    [...gen()]
    Array [ "a", "b", "c", "d", "e" ]

    只要实现了Iterator接口的对象都可以使用yield*遍历,如下所示

    function* gen(){
        yield 1;
        yield 2;
        yield* [3,4,5,6,7];
        yield 10;
    }
    console.log([...gen()]);
    Array [ 1, 2, 3, 4, 5, 6, 7, 10 ]
    ----------------------------遍历嵌套函数-----------------------
    function* walkArr(arr){
        if(Array.isArray(arr)){
            for(let v of arr){
                yield* walkArr(v);
            }
        }else{
            yield arr;
        }
    }
    var w = walkArr([1,[2,[3,10,[9]]]]);
    [...w];
    Array [ 1, 2, 3, 10, 9 ]

    Generator函数就介绍到此咯

  • 相关阅读:
    微服务下的持续集成-Jenkins自动化部署GitHub项目
    JDK新特性-Lambda表达式的神操作
    ActiveMQ详细入门教程系列(一)
    程序员必须了解的知识点——你搞懂mysql索引机制了吗?
    面试之后,扼腕叹息。
    哎,这让人抠脑壳的 LFU。
    延迟消息的五种实现方案
    Java实现Kafka生产者和消费者的示例
    Pytorch训练时显存分配过程探究
    Python命令行参数定义及注意事项
  • 原文地址:https://www.cnblogs.com/zmxmumu/p/5621122.html
Copyright © 2011-2022 走看看