Generator(生成器) 学习理解与实践

7次阅读

共计 2659 个字符,预计需要花费 7 分钟才能阅读完成。

created at 2019-04-08
总结

异步编程解决方案
可理解为一种状态机,封装了多个内部状态
可返回一个指向内部状态的指针对象(遍历器对象 Interator),所以可理解为其是一个遍历器对象生成函数
yield(产出), 定义不同的内部状态,yield 后跟表达式。
yield 表达式只能放在 Generator 函数中
(yield 表达式) 整个没有返回值,next() 的参数可以被认为是上一个 yield 的返回值。
V8 引擎直接忽略第一次 next() 的参数,因为没有上一次 yield
调用 next 方法,是指针从函数头部或上一次停止的地方开始执行,直到遇到下一个 yield 表达式或 return
当 next 的返回值的 value 属性为 yield 后表达式的结果或 return 后的值,done 属性为 true 时表示遍历 yield 结束
对象中使用 const obj = {myGeneratorMethod(){}} 等同于 obj = {myGeneratorMethod:function (){}}
yield* 用于在一个 generator 函数中执行另一个 generator 函数,并且会将另一个的 yield 全部执行完毕才会继续执行当前 generator 中的 yield

基础语法
function* generatorTest() {
console.log(‘ 第一个 yield 之前的语句 ’);
yield ‘yield 1’
yield ‘yield 3: 在表达式中要加括号 ’ + (yield ‘yield 2: in expression’)
console.log(‘yield 之后 return 之前 ’);
return ‘return’
}

const gt = generatorTest()
console.log(gt);
// 遍历器对象

/*
* 第一次调用是从函数头部开始执行,如果没有 yield,也需要执行一次 next 才会执行这些语句
*/
console.log(gt.next());
// 第一个 yield 之前的语句
// {value: “yield 1”, done: false}

console.log(gt.next());
// {value: “yield 2: in expression”, done: false}

console.log(gt.next());
// {value: “yield 3: 在表达式中要加括号 undefined”, done: false}
// 上面出现 undefined 的原因是 next 没有加参数

console.log(gt.next());
// yield 之后 return 之前
// {value: “return”, done: true}

console.log(gt.next());
// {value: “undefined”, done: true}

实现斐波拉契数列
链接
给原生对象添加 Iterator
先展示 for of 对 generator 的作用
function* generatorForOf(){
yield 1;
yield 2;
return 3;
}
/*
* for of 可以遍历 Generator 生成的 Iterator 对象
* 这里不能再遍历 gt 因为它已执行完毕
* 不会遍历 return 的值
*/
for (let item of generatorForOf()) {
console.log(‘for of :’, item);
}
给原生对象添加 iterator, 让其可以被 for of 遍历
function* objectAddIterator(obj) {
const props = Reflect.ownKeys(obj)

for (const key of props) {
yield [key, obj[key]]
}
}

const nativeObj = {
a: 12,
b: 34,
[Symbol(‘symbol c’)]: 3
}

for (const [key, value] of objectAddIterator(nativeObj)) {
console.log(typeof key === ‘symbol’ ? key.description : key, value);
}
应用场景
异步 ajax 请求
代码中的 loading 函数代码行数较多,也并非关键代码,所以不作展示。查看 loading 代码
<div id=”ajax”> 初始数据 </div>
<button onclick=”getSomeList()”> 请求 ajax 数据 </button>
function requestData(callback) {
function ajaxFn() {
setTimeout(() => {
ar.next(‘ajax 返回结果 ’)
}, 2000);
}

function* asyncReqData() {
loading(true)
const result = yield ajaxFn()
callback(result)
loading(false)
}

const ar = asyncReqData()
ar.next()
}

function getSomeList() {
requestData((res) => {
document.getElementById(‘ajax’).innerHTML = res
})
}
控制同步操作的流程
function controlFlow() {
const child1 = [() => {console.log(‘–‘,1); return ‘return 1’}, () => {console.log(‘–‘,2); return ‘return 2’}]
const child2 = [() => {console.log(‘–‘,3); return ‘return 3’}, () => {console.log(‘–‘,4); return ‘return 4’}]
function* generatorControl(child) {
console.log(‘===========’);
for (let i = 0; i < child.length; i++) {
yield child[i]()
}
}
let parent = [{c: child1}, {c: child2}];

function* parentFn(p){
for (let i=0; i< p.length; i++){
yield* generatorControl(p[i].c);
}
}

for (let step of parentFn(parent)) {
console.log(step);
}
}

controlFlow()
本文包括对 generate 基础的学习与练习,与一些心得!【会持续学习,更新】
详细学习请移步下方参考链接
参考:
参考文章
欢迎交流 Github

正文完
 0