js的Generator
主要参考:
https://www.cnblogs.com/rogerwu/p/10764046.html
Generator 函数的概念
Generator 函数是协程在 ES6 的实现,最大特点就是可以交出函数的执行权(即暂停执行)
Generator函数的特征
1、function关键字与函数名之间有一个星号 * (推荐紧挨着function关键字)
2、函数体内使用 yield 表达式,定义不同的内部状态 (可以有多个yield)
3、直接调用 Generator函数并不会执行,也不会返回运行结果,而是返回一个遍历器对象(Iterator Object)
依次调用遍历器对象的next方法,遍历 Generator函数内部的每一个状态
{
// 传统函数
function foo() {
return 'hello world'
}
foo() // 'hello world',一旦调用立即执行
// Generator函数
function* generator() {
yield 'status one' // yield 表达式是暂停执行的标记
return 'hello world'
}
let iterator = generator() // 调用 Generator函数,函数并没有执行,返回的是一个Iterator对象
iterator.next() // {value: "status one", done: false},value 表示返回值,done 表示遍历还没有结束
iterator.next() // {value: "hello world", done: true},value 表示返回值,done 表示遍历结束
}
{
function* gen() {
yield 'hello'
yield 'world'
return 'ending'
}
let it = gen()
it.next() // {value: "hello", done: false}
it.next() // {value: "world", done: false}
it.next() // {value: "ending", done: true}
it.next() // {value: undefined, done: true}
}
yield 表达式
yield命令是异步两个阶段的分界线
它表示执行到此处,执行权将交给其他协程
yield 表达式只能用在 Generator 函数里面,用在其它地方都会报错
{
function* demo() {
console.log('Hello' + yield); // SyntaxError
console.log('Hello' + yield 123); // SyntaxError
console.log('Hello' + (yield)); // OK
console.log('Hello' + (yield 123)); // OK
}
}
yield 表达式用作参数或放在赋值表达式的右边,可以不加括号
{
function* demo() {
foo(yield 'a', yield 'b'); // OK
let input = yield; // OK
}
}
yield 表达式和return语句的区别
相似
都能返回紧跟在语句后面的那个表达式的值
区别:
return 语句不具备记忆位置的功能
每次遇到 yield,函数就暂停执行,下一次再从该位置继续向后执行
return 语句一个函数只能执行一次
而在 Generator 函数中可以有任意多个 yield
yield表达式
如果在 Generator 函数里面调用另一个 Generator 函数,默认情况下是没有效果的
没有效果的案例
只返回了自身的两个状态值
{
function* foo() {
yield 'aaa'
yield 'bbb'
}
function* bar() {
foo()
yield 'ccc'
yield 'ddd'
}
let iterator = bar()
for(let value of iterator) {
console.log(value)
}
// ccc
// ddd
}
yield 表达式使用
{
function* foo() {
yield 'aaa'
yield 'bbb'
}
function* bar() {
yield* foo() // 在bar函数中 **执行** foo函数
yield 'ccc'
yield 'ddd'
}
let iterator = bar()
for(let value of iterator) {
console.log(value)
}
// aaa
// bbb
// ccc
// ddd
}
next() 方法的参数
field表达式本身没有返回值,或者说总是返回undefined
next方法可以带一个参数,该参数就会被当作上一个yield表达式的返回值
案例解析
第一次
it.next()1、
let result = undefined// 变量声明提前2、
yield [3+5+6]// 返回yield表达式的值并暂停3、
result = ...// 由于被暂停,无法给result赋值第二次
it.next()1、
result = ...// 将第二次next的参数赋值给result,紧接着上一个yield 的下一行执行2、yield result // 返回yield的值并暂停
{
function* gen() {
let result = yield 3 + 5 + 6
yield result
}
let it = gen()
console.log(it.next()) // {value: 14, done: false}
console.log(it.next()) // undefined {value: undefined, done: false}
}
第一次调用next()中的参数无效案例
next方法的参数表示上一个yield表达式的返回值,所以在第一次使用next方法时,传递参数是无效的
{
function* gen() {
let result = yield 3 + 5 + 6
console.log(result)
yield result
}
let it = gen()
console.log(it.next(10)) // {value: 14, done: false}
console.log(it.next(3)) // 3 {value: 3, done: false}
}
yield 表达式如果用在另一个表达式中,必须放在圆括号里面
{
function* gen(x) {
let y = 2 * (yield (x + 1)) // 注意:yield 表达式如果用在另一个表达式中,必须放在圆括号里面
let z = yield (y / 3)
return x + y + z
}
let it = gen(5)
console.log(it.next()) // 正常的运算应该是先执行圆括号内的计算,再去乘以2,由于圆括号内被 yield 返回 5 + 1 的结果并暂停,所以返回{value: 6, done: false}
console.log(it.next(9)) // 上次是在圆括号内部暂停的,所以第二次调用 next方法应该从圆括号里面开始,就变成了 let y = 2 * (9),y被赋值为18,所以第二次返回的应该是 18/3的结果 {value: 6, done: false}
console.log(it.next(2)) // 参数2被赋值给了 z,最终 x + y + z = 5 + 18 + 2 = 25,返回 {value: 25, done: true}
}
与 Iterator 接口的关系
ES6 规定,默认的 Iterator 接口部署在数据结构的Symbol.iterator属性
简言之,一个数据结构只要具有Symbol.iterator属性,就可以认为是“可遍历的”
{
let obj = {}
function* gen() {
yield 4
yield 5
yield 6
}
// 为obj添加Symbol.iterator属性
// >> 传统对象没有原生部署 Iterator接口,不能使用 for...of 和 扩展运算符
// >> 现在通过给对象添加 Symbol.iterator 属性和对应的遍历器生成函数,就可以使用了
obj[Symbol.iterator] = gen
for(let value of obj) {
console.log(value)
}
// 4
// 5
// 6
// ...obj的赋值方式可以避免改变原obj中的值
console.log([...obj]) // [4, 5, 6]
}
for...of循环
由于 Generator 函数运行时生成的是一个 Iterator 对象,因此,可以直接使用 for...of 循环遍历,且此时无需再调用 next() 方法
注意:
一旦 next() 方法的返回对象的 done 属性为 true,for...of 循环就会终止,且不包含该返回对象
{
function* gen() {
yield 1
yield 2
yield 3
yield 4
return 5
}
for(let item of gen()) {
console.log(item)
}
// 1 2 3 4
}
Generator的return 方法
可以返回给定的值(若没有提供参数,则返回值的value属性为 undefined),并且终结遍历 Generator 函数
{
function* gen() {
yield 1
yield 2
yield 3
}
let it = gen()
it.next() // {value: 1, done: false}
it.return('ending') // {value: "ending", done: true}
it.next() // {value: undefined, done: true}
}
应用案例
参照:
https://www.cnblogs.com/rogerwu/p/10764046.html 网页最后的案例

浙公网安备 33010602011771号