Generator 是与 Promise 同时由 ES6 引入标准的语法,最早由社区提出和实现
主要用于实现一种新的状态机制管理,让一段代码逻辑可以动态控制分段执行
Generator 新增关键字及函数
* 关键字
* 关键词用于生成 Generator函数(* 前后的空格没有影响),但有以下几个限制:
-
只能用于
function关键字 - 不能用于箭头函数
function * fun1 () {} // work
function* fun2 () {} // work
function *fun3 () {} // work
const fun4 = function * () {} // work
const fun5 = *() => {} // 报错!
一旦函数被生成为 Generator函数,这个函数就不能作为构造函数被 new 关键字使用了
function * Fun() {}
const obj = new Fun() {} // 报错!
yield 关键字
yield 的返回值
yield 在功能上和 return 有些类似,都用于将值返回,但有些差异:
-
return语句之后无法进行任何操作;yield后续的程序可以通过调用next继续执行 -
return无法和赋值符一起使用;yield可以和赋值符一起使用 -
return可以独立使用;yield必须配合Generator函数和next函数才能使用
function * test() {
const first = yield 1
const second = yield 2
const third = yield 3
}
const obj = test()
obj.next() // {value: 1, done: false}
obj.next() // {value: 2, done: false}
obj.next() // {value: 3, done: false}
obj.next() // {value: undefined, done: true}
yield 可以拼接的表达式
yield 后面可以拼接多种类型的值
- 基本类型:
yield 123、yield 123、yield { name: Peter }等等 - 函数:
yield function test(){} - 表达式:
yield 1 + 2 + 3
多个 yield 拼接
yield 和 yield 拼接时,后一个 yield 作为变量使用,为 undefined
function * test() {
yield yield
}
const obj = test()
obj.next() // {value: undefined, done: false}
yield 的类函数形式
看一段代码
function * test() {
yield 1
}
// 上面的写法,等价于下方的写法
function * test() {
yield(1)
}
yield(1) 咋一看,会让人觉得是 yield 作为一个函数被调用了
实则是 yield 返回了一个使用小括号包裹的表达式(1)
这里纯粹是个人见解,没有找到权威的文档佐证,欢迎指正!
next 函数
第一次 next 调用的传参没有作用
function *test(x) {
const re = yield 1 + x;
return re + 2;
}
const obj = test(5);
obj.next(2) // {value: 6, done: false}
obj.next(3) // {value: 5, done: true}
如上
- 变量
x来自生成Generator对象时的参数5 - 第一次
next调用,函数内部开始执行到 yield 返回1 + 5,因此本次next参数无效 - 第二次
next调用,next 的参数 3 赋值给 yield 后面的整个表达式const re = 3,因此结果为3 + 2
next 函数参数是对上一个 yield 及后方整个表达式的值覆盖
function *test() {
console.log(yield 5) // 2
}
const obj = test();
obj.next()
obj.next(2)
第二次 next 调用,yield 5 的值为 2,被参数覆盖
yield 和 next 的返回值
对于 yield 和 next 的返回值问题一直比较模糊
{ value: any, done: Boolean } 到底是 yield 的返回值还是 next 的返回值?
下面分别引用了 MDN 上对 yield 和 next 返回值的描述
yield关键字实际返回一个IteratorResult对象,它有两个属性,value和done。value属性是对yield表达式求值的结果,而done是false,表明生成器函数尚未完全完成。
参考自yield
next() 方法返回一个包含属性 done 和 value 的对象。该方法也可以通过接受一个参数用以向生成器传值。
参考自Generator.prototype.next()
说实话,从两段描述上来看,yield 和 next 返回值都是 { value: any, done: Boolean }
所以写了下方的代码做验证
function *test() {
console.log(yield 1) // 2
}
const obj = test();
obj.next()
obj.next(2) // { value: undefined, done: true }
从结果上来看
-
yield返回的是value -
next返回的是{ value: undefined, done: true }
Generator 执行流程
- 生成
Generator对象,这时候Generator函数内部不会执行
function *test() {
console.log( start )
let re = yield 1;
return re + 2;
}
const testObj = test(); // no console
const testObj2 = test(); // no console
如果生成多个 Generator对象,则各个 Generator对象 保持着各自的执行阶段,互不影响
-
Generator对象调用next函数
从函数开头或上一个 yield 开始,执行到下一个 yield 或者 return
function *test() {
console.log( before first yield )
let first = yield 1;
console.log( after first yield )
let second = yield 2;
console.log( after second yield )
let third = yield 3;
console.log( after third yield )
return re + 2;
}
const testObj = test(); // no console
const firstResult = testObj.next(); // before first yield
const secondResult = testObj.next(); // after first yield
const thirdResult = testObj.next(); // after second yield
const fourthResult = testObj.next(); // after third yield
yield 委托迭代
* 和 yield 组合使用,可以将多个生成器连接在一起
function * anotherGenerator(i) {
yield i + 1;
yield i + 2;
yield i + 3;
}
function * generator(i) {
yield* anotherGenerator(i);
}
var gen = generator(1);
gen.next().value; // 2
gen.next().value; // 3
gen.next().value; // 4
类型判断
业界常见的判断 Generator 对象和函数的方法
如何判断 Generator 对象
function isGenerator(obj) {
return obj && typeof obj.next === function && typeof obj.throw === function ;
}
这里运用鸭子模型进行判断
如果对象中有 next 与 throw 两个方法,那么就认为这个对象是一个生成器对象
如何判断 Generator 函数
function isGeneratorFunction(obj){
var constructor = obj.constructor;
if(!constructor) return false;
if(
GeneratorFunction === constructor.name ||
GeneratorFunction === constructor.displayName
) return true;
return isGenerator(constructor.prototype);
}
利用函数的 constructor 构造器的名字来判断(name 与 displayName 为了处理兼容性)
这里递归调用 isGenerator 判断 constructor 的原型是由于有自定义迭代器的存在
总结
Generator 函数作为一种新的状态机制管理,让一段代码逻辑可以动态控制分段执行,有独到的作用和使用场景
但如果独立使用,操作繁琐,语义也不清晰
所以在 ES6 引入后,社区使用例如 co 库进行二次封装
在前端异步编程领域,开阔思路的作用大于实际应用,有点昙花一现的意思
为 Async/Await 做铺垫
















暂无评论内容