继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

js异步编程之Generator

FFIVE
关注TA
已关注
手记 279
粉丝 70
获赞 458

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 的异步编程解决方案



作者:黎贝卡beka
链接:https://www.jianshu.com/p/92639e681e2a


打开App,阅读手记
0人推荐
发表评论
随时随地看视频慕课网APP