Generator

目录

了解Generator

了解Iteartor

next()

return()

for…of 循环

yield* 表达式

next传参

使用场景:抽奖

使用场景:回调地域


了解Generator

Generator 函数(也可以叫做生成器函数)是ES6 提供的一种异步编程解决方案。

声明

        与函数声明类似,不同的是function关键字与函数名之间有一个星号,以及函数体内部使用yield表达式。

调用

        Generator函数调用之后不会立即执行,而是会返回遍历器对象(Iterator对象)。

        通过遍历器对象的next方法来遍历内部yield表达式定义的每一个状态,会返回一个有着value和done两个属性的对象。

        value属性是yield表达式后面那个表达式的值;done属性是一个布尔值,表示是否遍历结束。

        每次调用Iterator对象的next方法时,内部的指针就会从函数的头部或上一次停下来的地方开始执行,直到遇到下一个 yield 表达式或return语句暂停。

了解Iteartor

Iteartor的遍历过程和C++语言一样

  1)创建一个指向数据结构起始位置的指针。(起始位置不是第一个成员的位置,起始位置是一个单独的标志位。)

  2)当调用next()方法,指针就向后移动一个位置,并返回当前位置上的成员,直到指针指向数据结构的结束位置为止。

        第二步中,js语言返回的的成员信息是两个,value和done,value不用介绍,done是一个表示遍历是否结束的布尔值。

next()

  function* myGenerator1() {
        yield 'Hello';
        yield 'world';
        return 'ending';
    }

    let mg1 = myGenerator1(); // MG:遍历器对象
    console.log(mg1.next());  // {value: 'Hello', done: false}
    console.log(mg1.next());  // {value: 'world', done: false}
    console.log(mg1.next());  // {value: 'ending', done: true}
    console.log(mg1.next());  // {value: undefined, done: false}
    // 还可以使用 for... of 循环遍历 Generator 函数生产的 Iterator 对象。

return()

   function* myGenerator2() {
        yield 1;
        yield 2;
        yield 3;
    }
    let mg2 = myGenerator2();
    mg2.next();         // {value: 1, done: false}
    mg2.return('hi');   // {value: 'hi', done: true} Generator 函数的遍历就此终止
    mg2.next();

for…of 循环

for of循环可以自动遍历 Generator 函数运行时生成的Iterator对象,且此时不再需要调用next方法。
需要注意,一旦 next() 方法的返回对象的 done 属性为 true,for…of 循环就会终止,且不包含该返回对象


    function* myGenerator3() {
        yield 1;
        yield 2;
        return 5;
    }
    for(let item of myGenerator3()) {
        console.log(item);   // 1 2
    }

yield* 表达式

yield* 表达式用来在一个 Generator 函数里面 执行 另一个 Generator 函数

  function* myGenerator4() {
        yield 1;
        yield 2;
    }
    function* myGenerator5() {
        // myGenerator4(); // 打印 3 4 
        yield* myGenerator4();   // 在myGenerator5函数中执行另一个Generator函数     打印: 1 2 3 4 
        yield 3;
        yield 4;
    }
    let mg5 = myGenerator5();
    for(let value of mg5) {
        console.log(value);
    }

yield*表达式后也可以跟着一个可遍历对象,例如Array,String,Map,Set对象。

function* myGenerator6(){
        let val;
        val = yield * [1,2];
        console.log('end');
    }

    const mg6 = myGenerator6();
    mg6.next() // {value:1, done:false}
    mg6.next() // {value:2, done:false}
    mg6.next() // {value:undefined, done:true}

next传参

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

这里不能把next执行结果中的value值与yield返回值搞混了,它两不是一个东西~~~

 function* fn(x) {
        let y = 2 * (yield (x + 1));
        let z = yield (y / 3);
        return (x + y + z);
    }

    let f1 = fn(5);
    console.log(f1.next());  // {value: 6, done: false}
    console.log(f1.next());  // {value: NaN, done: false}
    console.log(f1.next());  // {value: NaN, done: true}

    let f2 = fn(5);
    console.log(f2.next());  // {value: 6, done: false}
    console.log(f2.next(12)); // {value: 8, done: false}
    console.log(f2.next(13)); // {value: 42, done: true}

f1 第二次调用value为NaN是因为yield本身没有返回值,导致y为undefined后面计算为NaN

f2 的第二步通过next传参12,12做为上一步yield表达式的返回值进行计算,此时y 等于 24 (2 * 12),所以返回值value 等于8。第三步传入13做为上一步的返回值,即 z 等于13;   

x: 5, y: 24    z: 13,相加运算返回value: 42

使用场景:抽奖

有一个抽奖活动,总共抽6次,每抽一次,抽奖机会递减

 按照常规做法就需要声明一个全局的变量来保存剩余的可抽奖次数,而全局变量会造成全局污染,指不定什么时候就被重新赋值了,所以不推荐

事实上,抽奖通常是每个人自己来抽,每抽一次就调用一次抽奖方法,而不是点一次就一次性就全部运行完,是可暂停的,这个不就是 Generator 函数的意义所在吗?

具体抽奖逻辑的方法

 <button id="btn">开始抽奖</button>

function draw(count) {
    // 执行一段抽奖逻辑...
    console.log(`剩余${count}次`);
  }
  // 执行抽奖的方法
  function* remain(count) {
    while(count > 0) {
      count--;
      yield draw(count);
    }
  }
  let startDrawing = remain(6);
 
  document.getElementById('btn').addEventListener('click', function(){
    startDrawing.next();
  }, false)

使用场景:回调地域

// 回调地域:
    setTimeout(() => {
        console.log(111);
        setTimeout(() => {
            console.log(222);
            setTimeout(() => {
                console.log(333);
            }, 3000)
        }, 2000)
    }, 1000)
// 使用generator
    function one() {
        setTimeout(() => {
            console.log(111);
            iterator.next(); //执行第二个暂停点
        },1000)
    }
    function two() {
        setTimeout(() => {
            console.log(222); 
            iterator.next(); //执行第三个暂停点
        },2000)
    }
    function three() {
        setTimeout(() => {
            console.log(333);
        },3000)
    }

    function * gen() {
        yield one();
        yield two();
        yield three();
    }
    let iterator = gen();
    iterator.next(); //执行第一个暂停点

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值