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 网页最后的案例