generator必须配合一个关键字yield才能使用
generator将一个函数封装成了多个片段,一个yield是一个片段
在函数内部只要碰到yield关键字就会停止运行
只要在generator实例化对象后面调用next指针就会恢复执行
generator是如何向外进行产出的呢?——原理
- 通过
yield
关键字进行产出
先生成一个generator函数
generator
函数一执行就会生成generator
实例化对象(不用new
)在
generator
实例化对象身上调用next()
的方法就可以使yield
不断产出内容
yield
关键字默认返回值为undefined
yield
关键字有两个功能:
- 会给指针方法赋值——即yield产出的值会赋值给产出的结果(
next()
);- 但是
yield
在代码里有默认值,在操作完后会返回undefined
——即代码中a + b 为 undefined + undefined
即结果为NAN
- 当
next()
调用超出 yield 就没有产出,done:true
<script>
function *gen() {
let a = yield 6;
let b = yield 8;
let c = yield a + b;
yield console.log(c);
}
let g = gen();
console.log(g.next());
//Object { value: 6, done: false }
console.log(g.next());
//Object { value: 8, done: false }
console.log(g.next());
//Object { value: NaN, done: false }
console.log(g.next());
//undefined
console.log(g.next());
//Object { value: undefined, done: true }
</script>
在generator中yiled以及return都能进行产出
碰到
yield done:false
碰到
return done:true
——即产出完毕; 即终止状态,一般很少使用return
<script>
function *gen() {
let a = yield 6;
return 22;
let b = yield 8;
}
let g = gen();
console.log(g.next());
//Object { value: 6, done: false }
console.log(g.next());
//Object { value: 22, done: true }
console.log(g.next());
//Object { value: undefined, done: true }
</script>
generator
改变了程序执行的流程本身代码是连续执行的,但是成为片段后,需要调用指针才能执行,不调用就不执行
generator的
核心是在何时调用next()
——何时进行产出
next()
里的参数会做为上一次yield
关键字表达式的返回值进行使用相当于
yield
产出后默认返回值undefined
替换成了参数
<script>
function *gen() {
let a = yield 6;
let b = yield 8;
let c = yield a + b;
return 123;
}
let g = gen();
console.log(g.next());
//Object { value: 6, done: false }
console.log(g.next(55));
//Object { value: 8, done: false }
console.log(g.next(66));
//Object { value: 121, done: false }
console.log(g.next());
//Object { value: 123, done: true }
</script>
yield后面是函数不影响,还是相当于一个值
<script>
function *gen() {
let a = yield 6;
let b = yield function *() {
let f = yield 56;
let e = yield 99;
let g = yield f + e;
}
let c = yield a + b;
return 44444
}
let g = gen();
console.log(g.next());
//Object { value: 6, done: false }
console.log(g.next(55));
//Object { value: * b(), done: false }
console.log(g.next(66));
//Object { value: 121, done: false }
console.log(g.next());
//Object { value: 44444, done: true }
</script>
如果在
yield
后面加*
这个函数不可遍历——is not iterable
<script>
function *gen() {
let a = yield 6;
let b = yield* function *() {
let f = yield 56;
let e = yield 99;
let g = yield f + e;
}
let c = yield a + b;
return 44444
}
let g = gen();
console.log(g.next());
console.log(g.next(55));
console.log(g.next(66));
console.log(g.next());
</script>
generator
函数生成一个generator
实例化对象
generator
一般指generator
实例化对象,generator
函数的目的是为了生成generator
实例化对象
generator
函数全称叫做generator
生成函数
<script>
function *gen() {
let a = yield 6;
function *foo() {
let f = yield 56;
let e = yield 99;
let g = yield f + e;
};
//generator 函数生成一个 generator 实例化对象
//yield 后面不带 * 就产出一个 generator 实例化对象
let b = yield foo();
let c = yield a + b;
return 44444
}
let g = gen();
console.log(g.next());
//Object { value: 6, done: false }
console.log(g.next(55));
//Object { value: Generator, done: false }
console.log(g.next(66));
//Object { value: 121, done: false }
console.log(g.next());
//Object { value: 44444, done: true }
</script>
递归产出(yield*
)
yield*
*
遍历的对后面的generator
指针进行自动的调用——就是进入到上面的generator
函数里进行yield
调用
<script>
function *gen() {
let a = yield 6;
//不是普通函数,是 generator 函数
function *foo() {
let f = yield 56;
let e = yield 99;
let g = yield f + e;
};
//yield后面带*后已经不叫产出了, 往函数里面进行作用
let b = yield* foo();
let c = yield a + b;
return 44444
}
let g = gen();
console.log(g.next());
//Object { value: 6, done: false }
console.log(g.next());
//Object { value: 56, done: false }
console.log(g.next(66));
//Object { value: 99, done: false }
console.log(g.next(55));
//Object { value: 121, done: false }
</script>
generator 跟 iterator其实是一个东西
generator 实际上就是 iterator 的语法糖
generator的遍历器接口就是该函数本身
g[Symbol.iterator]
不在g
身上,在原型身上
generator
所有的遍历器接口都是一样的——g[Symbol.iterator]
for...of
自动调用next()
,并且根据产出值done
为false
还是true
确定是否继续遍历
<script>
function *gen() {
let a = yield 6;
function *foo() {
let f = yield 56;
let e = yield 99;
let g = yield f + e;
};
let b = yield* foo();
let c = yield a + b;
return 44444
}
var g = gen();
//g[Symbol.iterator]不在g身上,在原型身上
g[Symbol.iterator] === gen
console.log(g);
//for...of 自动调用 next()
for (let some of gen()) {
console.log(some);
}
//66
//55
//99
//NaN
</script>
for...of
的原理(为了更好的理解generator
的遍历器接口就是该函数本身)
- 先查看是否具备遍历器接口,如果有则调用接口并返回遍历器对象,反之报错
- 调用遍历器对象中的
next
方法,将该方法返回的对象中的value
赋值给目标变量,根据返回的对象的done
属性来决定是否进行下次遍历for...of
只保留done
为false
的value
<script>
let index = 0;
let o = {
next: function() {
return {
value: index++,
done: index > 5 && true
}
},
[Symbol.iterator]: function() {
return {
next: this.next
}
}
}
for (let some of o) {
console.log(some);
}
</script>
generator
函数的this
generator
指的就是generator
函数,就是带*
的函数
generator
返回的就是iterator
遍历器对象仍然是点在谁身上指向谁
<script>
let arrLike = {
0: 66,
1: 88,
2: 33,
3: 456,
length: 4,
//生成的generator对象自带next() 方法
[Symbol.iterator]: function *() {
for (let i = 0; i < this.length; i++) {
yield this[i] * i;
}
return;
}
}
for (let some of arrLike) {
console.log(some);
}
</script>
return()方法
return
方法是手动触发generator
停止产出只要记住
return
停止方法后返回value:undefined, done:false
,表示产出结束,后面的yield
全部不执行
<script>
function* gen () {
yield 33;
yield 66; //return yield 66;
yield 99;
return 520
}
let g = gen();
// reture 方法将指针一下调到了最后
// 里面传参的话,返回值的value就是里面的参数
console.log(g.next());
//相当于在该位置上最前面补上一个 return
console.log(g.return());
</script>
throw()
方法
异常处理
<script>
function* gen () {
yield 33;
yield 66; //return yield 66;
yield 99;
return 520
}
let g = gen();
console.log(g.next());
//触发了错误
//与return类似
console.log(g.throw("throw error"));
//Uncaught throw error
</script>
throw()
抛出的错误既可以在generator
外部捕获也可以在generator
内部捕获
外部捕获
<script>
function* gen () {
yield 33;
yield 66; //return yield 66;
yield 99;
return 520
}
let g = gen();
console.log(g.next());
//会用try{}catch(error){}不会终止后面的代码
try{
console.log(g.throw("throw error"));
} catch (error) {
console.log(error);
}
console.log("++++++++++");
</script>
内部捕获
<script>
function* gen() {
try {
console.log("--------------");
//--------------
yield 33;
yield 99;
console.log("++++++++++++++++");
//+++++++++++++++++
return 520
} catch (error) {
console.log("内部捕获");
//内部捕获
}
}
let g = gen();
console.log(g.next());
//Object { value: 33, done: false }
console.log(g.throw());
//Object { value: undefined, done: true }
console.log(g.next());
//Object { value: undefined, done: true }
</script>
- generator 创建之初是为了调控代码的执行结构
- 异步操作,同步写法
- 以前写异步总是回调,一层一层的
- 以后回调函数都会消失