Generator
- 一、Generator 是什么?
- 1.1 与普通函数写法不一样,有两个不同
- 二、Generator 使用
- 2.1 书写方法
- 三、yield语句
- 3.1 yield和return
- 3.2 注意事项
- 3.3 yield*语句
- 3.4 yield*应用
- 四、next方法
- 4.1参数
- 总结
一、Generator 是什么?
Generator 函数是 ES6 提供的一种异步编程解决方案,最大特点交出函数的执行权。
和普通函数不一样的是必须调用next()才会执行函数。
1.1 与普通函数写法不一样,有两个不同
- function与函数名之间有个(*)号;
不同于普通函数,可暂停执行,所以加*
区别。 - Generator 函数体内部使用yield语句,可以定义不同的内部状态,其实就是数据不同。
函数内部的状态,就是函数内部的值,它在不同的时候是不一样的。
本质上整个Generator函数就是一个封装的异步任务,或者说是异步任务的容器。
yield的命令是异步不同阶段的分界线,有时把yield的当成return。
二、Generator 使用
2.1 书写方法
- 函数名和方法中间有个
*
号,但没规定放置的位置,一般跟在function后面;
- 调用Generator 函数,函数并不执行,返回的是一个指向内部状态指针的对象(遍历器对象)
function* test(){
let n = 1;
yield n;
yield++n;
yield--n;
return n;
}
let t = test();
console.log(t); //test {<suspended>}
返回的是一个对象,并且有next, return,throw方法
- 想打印值,必须调用next方法,使指针下一个状态。也就是每次调用都从上一个指针状态(yield)开始,遇到下一个yield或return语句为止。也就是一段段段执行,yield暂停,next继续执行。
下面调用6次,一个yield一个状态。返回一个对象,value代表值,done代表是否遍历结束(true结束,false未结束)
done为true已经代表函数结束,再打印几次值都为undefined
console.log(t.next()); //{value: 1, done: false}
console.log(t.next()); //{value: 2, done: false}
console.log(t.next()); //{value: 1, done: false}
console.log(t.next()); //{value: 1, done: true}
console.log(t.next()); //{value: undefined, done: true}
console.log(t.next()); //{value: undefined, done: true}
三、yield语句
yield语句就是暂停标志。
3.1 yield和return
相同点:
- 都能返回后面表达式的值。
不同点:
- yield的函数暂停执行时,下一次会从该位置继续向后执行,并且一个函数可有多个yield语句。
- return返回就相当于结束函数,一个函数只有一个return。
3.2 注意事项
- yield后面n+1不会执行,只有调用next方法才会执行。
function* test() {
let n = 1;
yield n + 1;
console.log(n);
}
test(); //此时打印是空白的
//必须调用next方法才会执行
console.log(test().next()); //{value: 2, done: false}
2.可以不用yield,但是必须调用next方法才会执行
function* test() {
let n = 1;
console.log(n);
}
let t = test();
console.log(t.next());
- yield语句不能用在普通函数中。
function test() {
yield 1;
}
console.log(test()); // Unexpected number
3.3 yield*语句
Generator 函数内部调用另一个Generator 函数,默认情况无效。
function* A(){
yield 1;
yield 2;
}
function* B(){
yield 3;
A();
yield 4;
}
for(let i of B()){
console.log(i)
}
//3
//4
虽然在B函数中调用了A(),但只能打印出3,4。如果想要调用,就要在前面使用yield*语句。
function* A(){
yield 1;
yield 2;
}
function* B(){
yield 3;
yield* A();
yield 4;
}
for(let i of B()){
console.log(i)
}
//3
//1
//2
//4
yield*是for…of…的一种简写形式,我们知道for…of…可以遍历出具有iterator接口的数据结构(数组,类数组对象,字符串,nodelist等)
3.4 yield*应用
- 数组扁平化
let a = [1,[1,2],[1,2,3]]
function* iterTree(arr){
if(Array.isArray(arr)){
for(let i = 0;i<arr.length;i++){
yield* iterTree(arr[i]);
}
}else{
yield arr;
}
}
for(let i of iterTree(a)){
console.log(i)
}
- 遍历完全二叉树
// 构建树
function Tree(left,middle,right){
this.left = left;
this.middle = middle;
this.right = right;
}
// 中序遍历函数
function* inorder(t){
if(t){
yield* inorder(t.left);
yield t.middle;
yield* inorder(t.right);
}
}
// 生成二叉树
function make(array){
if(array.length == 1) return new Tree(null,array[0],null);
return new Tree(make(array[0]),array[1],make(array[2]));
}
let tree = make([[['a'],'b',['c']],'d',[['e'],'f',['g']]]);
// 遍历二叉树
var result = [];
for(let node of inorder(tree)){
result.push(node);
}
console.log(result) //['a', 'b', 'c', 'd', 'e', 'f', 'g']
四、next方法
next方法是使指针指向下一个状态。除了for…of遍历出来,必须调用next才能打印出yield值。
4.1参数
yield语句本身没有返回值,或者总返回undefined。next方法可以带一个参数,这个参数会被当上一个yield语句的返回值。
function* test(x) {
var a = yield x-1;
var b = yield (a+2);
return a+b;
}
let t = test(4);
console.log(t.next()) //{value: 3, done: false}
console.log(t.next(10)) //{value: 12, done: false}
console.log(t.next(4)) //{value: 14, done: true}
第一步:传个4进去,x-1=3,打印出来为3
第二步:传个10进去,这个参数会被当上一个yield语句的返回值,也就是a的值为10,a+2,打印出来为12。
第三步:传个4进去,上一个参数b的值为4,a的值为之前的10,a+b为14。
总结
Generator 函数是一种异步编程解决,可以更好的控制函数执行。其中yield是同步的,调用next()才能打印出值。
函数返回的是一个对象,而next方法返回的也是一个对象,其中有value(值),done(是否遍历完)。
一个next对应一个yield语句,当最后done为true时,再调用next方法,值都是undefined。全部遍历可使用for…of,其原理还是数据结构内部有iterator接口。