JavaScript中的Generator函数
前言 🔗
在学习Async
的时候可以先学习generator
函数
generator
是ES6提出的一种特性,基于这个特性,可以去分段的执行函数
也就是函数执行到某一行,跳出之后可以重新进入到上次的状态
Generator 🔗
Generator 意思为生成器
在js中,Generator是一种特殊的函数
Generator的关键字为在function
和括号之间加一个*
号
以及在函数体内使用yield
关键字
function* fn() {
yield 1;
}
简单点讲,生成器函数可以在yield
的位置暂停执行,返回yield
紧接着的表达式的值
运行一个Generator函数会返回一个生成器对象
通过调用next
方法可以得到迭代器(Iterator)对象
可以通过next
放方法获取下一个yield
的值
function* fn() {
yield 1;
yield 2;
return 3;
}
const iterator = fn();
console.log(iterator.next()); // { value: 1, done: false }
console.log(iterator.next()); // { value: 2, done: false }
console.log(iterator.next()); // { value: 3, done: true }
通过执行函数得到一个生成器对象,这时候函数是还没有执行的
需要通过不断地调用next
函数来进行执行
可以看出,next
返回的对象value
属性的值对应yield
后面的表达式的值
而done
则是表明此时迭代是否结束的标志
这也就是迭代器的结构
如果我们不写return 3
,而是直接返回的话,就可以使用循环来判断是否到达了迭代的末尾
function* fn() {
yield 1;
yield 2;
return;
}
const iterator = fn();
let item = iterator.next();
while(!item.done){
console.log(item.value); // 以此输出 1 2
item = iterator.next();
}
当然,我们可以给每次调用的next
传入参数,传入的参数作为yield
的返回值
function* fn() {
const r = yield 1;
yield r;
return;
}
const iter = fn();
console.log(iter.next('我是传入的参数')); // { value: 1, done: false }
console.log(iter.next()); // { value: '我是传入的参数', done: false }
console.log(iter.next()); // { value: undefined, done: true }
generator
有什么好处呢
比如我们现在需要一个函数来生成唯一id的话
不使用generator
可以这么写
let id = 0;
function getId() {
id++;
return id;
}
getId(); // 1
getId(); // 2
getId(); // 3
由于函数无法记忆状态,所以只能将变量放在全局
这样会造成变量污染
还可以使用闭包来改进上面的函数
const getId = (function getId() {
let id = 0;
return function (){
id++;
return id;
}
})();
getId(); // 1
getId(); // 2
getId(); // 3
如果使用generator
,看起来会更加直观
function* getId() {
let id = 1;
let ct = yield id;
while(!ct) {
id++;
ct = yield id;
}
}
const gen = getId();
gen.next(); // 1
gen.next(); // 2
gen.next(); // 3
gen.next(true); // undefined 不想自增了,结束掉
在MDN上可以看到,除了next
方法之外
generator
对象还有return
和throw
return
函数用于直接结束生成器,即把生成器置于完成状态
function* fn() {
yield 1;
yield 2;
return;
}
const gen = fn();
gen.next(); // { value: 1, done: false }
gen.return(); // { value: undefined, done: true }
gen.next(); // { value: undefined, done: true } 生成器结束了,next也就是返回最后的结果,这个最后的结果可能是执行到结束的结果,也可能是调用return的结果
gen.return(3); // { value: 3, done: true } 可以重复地调用return,根据参数返回对应的值
throw
用于向生成器抛出一个异常
function* fn() {
try {
yield 1;
yield 2;
} catch (e) {
console.log(e); // 输出给你一个大嘴巴子
}
}
const gen = fn();
gen.next(); // 1;
gen.throw(new Error('给你一个大嘴巴子'));
throw
返回的也是一个迭代器对象
如果此时生成器还能继续到达下一个yield
的话
那么返回的就是下一个yield
如果不能到达,那么返回的迭代器对象里面的done
为true
function* fn() {
try {
yield 1;
yield 2;
} catch (e) {
console.log(e); // 输出给你一个大嘴巴子
}
yield 3;
}
const gen = fn();
gen.next(); // 1;
gen.throw(new Error('给你一个大嘴巴子')); // 报错,返回了 { value: 3, done: false }
需要注意,ie不支持generator
这个特性
后记 🔗
惆怅,许愿一个实习…