js异步编程之Generator
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来暂停。
.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
可以通过向
.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
共同学习,写下你的评论
评论加载中...
作者其他优质文章