js异步编程之Generator

作者: 黎贝卡beka | 来源:发表于2018-08-06 21:01 被阅读3次

    Generator介绍

    Generator 的中文名称是生成器,它是ECMAScript6中提供的新特性。在过去,封装一段运算逻辑的单元是函数。函数只存在“没有被调用”或者“被调用”的情况,不存在一个函数被执行之后还能暂停的情况,而Generator的出现让这种情况成为可能。

    通过function*来定义的函数称之为“生成器函数”(generator function),它的特点是可以中断函数的执行,每次执行yield语句之后,函数即暂停执行,直到调用返回的生成器对象的next()函数它才会继续执行。

    也就是说Generator 函数是一个状态机,封装了多个内部状态。执行 Generator 函数返回一个遍历器对象(一个指向内部状态的指针对象),调用遍历器对象的next方法,使得指针移向下一个状态。每次调用next方法,内部指针就从函数头部或上一次停下来的地方开始执行,直到遇到下一个yield表达式(或return语句)为止。

    yield关键字

    真正让Generator具有价值的是yield关键字,这个yield关键字让 Generator内部的逻辑能够切割成多个部分。

    let compute = function* (a, b) {
      let sum = a + b;
      yield console.log(sum);
      let c = a - b;
      yield console.log(c);
      let d = a * b;
      yield console.log(d);
      let e = a / b;
      console.log(e);
    };
    // 执行一下这个函数得到 Generator 实例
    let generator = compute(4, 2);
    // 要使得函数中封装的代码逻辑得到执行,还得需要调用一次next方法。
    generator.next();
    
    ----------
    var compute = function* (a, b) {
      let sum = a + b;
      yield sum;
      let c = a - b;
      yield c;
      let d = a * b;
      yield d;
      let e = a / b;
      e;
    };
    

    输出结果:
    6
    {value: undefined, done: false}

    发现函数执行到第一个yield关键字的时候就停止了。要让业务逻辑继续执行完,需要反复调用.next()

    generator.next();
    generator.next();
    generator.next();
    generator.next();
    

    可以简单地理解为yield关键字将程序逻辑划分成几部分,每次.next()执行时执行一部分。这使得程序的执行单元再也不是函数,复杂的逻辑可以通过yield来暂停

    1. .next()调用时,返回一个对象

    yield除了切割逻辑外,它与.next()的行为息息相关。每次.next()调用时,返回一个对象,这个对象具备两个属性。
    其中一个属性是布尔型的done。它表示这个Generator对象的逻辑块是否执行完成。
    另一个属性是value,它来自于yield语句后的表达式的结果。

    function * getNumbers(num) {
        for(let i=0; i<num;i++) {
            yield i
        }
        return 'ok';
    }
    const gen = getNumbers(10);
    function next() {
        let res = gen.next();
        console.log(res);
        if(res.done) {
            console.log('done');
        } else {
            setTimeout(next,300)
        }
    }
    next();
    

    输出结果:
    {value: 0, done: false}
    undefined
    {value: 1, done: false}
    {value: 2, done: false}
    {value: 3, done: false}
    {value: 4, done: false}
    {value: 5, done: false}
    {value: 6, done: false}
    {value: 7, done: false}
    {value: 8, done: false}
    {value: 9, done: false}
    {value: "ok", done: true}
    done

    1. 可以通过向.next()传递参数
    let compute = function* (a, b) {
      var foo = yield a + b;
      console.log(foo);
    };
    
    let generator = compute(4, 2);
    generator.next(); // {value: 6, done: false}
    generator.next("Hello world!"); //Hello world! {value: undefined, done: true}
    

    通过.next()传递参数,可以赋值给yield关键字前面的变量声明。

    所以,对于Generator而言,它不仅可以将逻辑的单元切分得更细外,还能在暂停和继续执行的间隔中,动态传入数据,使得代码逻辑可以更灵活。

    使用 Generator 编写状态切换逻辑代码

    function * loop(list, max=Infinity) {
        for(let i=0; i<max;i++) {
            yield list[i % list.length];
        }
    }
    
    function toggle(...actions) {
        let gen = loop(actions); 
      //错误写法:先调用loop(actions).next(); 
        return function(...args) {
            return gen.next().value.apply(this, args);
        }
    }
    
    // switcher.addEventListener('click', toggle(
    //     e => e.target.className = 'off',
    //     e => e.target.className = 'on'
    // ));
    
    switcher.addEventListener('click', toggle(
        e => e.target.className = 'off',
        e => e.target.className = 'warn',
        e => e.target.className = 'on'
    ));
    

    完整代码:https://code.h5jun.com/yeyo/edit?html,css,js,output

    References

    Generator与异步编程
    基于 Generator 与 Promise 的异步编程解决方案

    相关文章

      网友评论

        本文标题:js异步编程之Generator

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