
背景
我们一直认为在 JavaScript 中,所有函数执行时不会被任何事情打断。ES6 引入了一种新型的函数形式,称为 。生成器在执行过程中可以暂停自身,并可以主动恢复,在每次暂停/恢复循环都提供了一次双向传递数据的机会。生成器是遵循迭代器协议以及可迭代协议的。
使用方法
简单使用
生成器通过 中断程序执行,并返回 yield 语句右边的值。生成器调用后会生成一个迭代器,迭代器 next 方法可以传值会生成器中。
function *foo(){
var a = yield 1;
var b = yield 2;
var c = yield 3;
return {a,b,c}
}
var it = foo()
it.next('a')
it.next('b')
it.next('c')
it.next('d')
/*
{ value: 1, done: false }
{ value: 2, done: false }
{ value: 3, done: false }
{ value: { a: 'b', b: 'c', c: 'd' }, done: true }
*/
语法规则
关键字将 next 传入的参数赋值给当前上下文。yield 是低运算优先级右结合的,当遇到左面的变量会抛出异常。
var a = yield 1;
//等价于
var a = blanck = 1;
//而下面这样是非法的
var a = 'hi' + yield 1;
//等价于
var a = 'hi' + blanck = 1;
yield*(yield委托)
yield*(yield委托),把生成器控制委托给迭代器,直到其耗尽。
function *foo(){
yield *[1,2,3]
}
let it = foo()
it.next() //{ value: 1, done: false }
it.next() //{ value: 2, done: false }
it.next() //{ value: 3, done: false }
生成器上附着的迭代器支持可选的 return(..) 和 throw(..) 方法。这两 种方法都有立即终止一个暂停的生成器的效果。
function *foo() {
yield 1;
yield 2;
yield 3;
}
var it = foo();
it.next(); // { value: 1, done: false }
it.return( 42 ); // { value: 42, done: true }
it.next(); // { value: undefined, done: true }
co (类 async,await)
TJ 大神著名的 库,正是结合 Promise 与 Generator 的特性,实现了异步代码同步执行。
co 通过传入生成器,再将生成器的调用权放在 Promise 中,通过生成器 可以双向传递数据实现。
function co(gen){
var it = gen();
return new Promise((resolve, reject) => {
onFufilled()
function onFufilled(res){
let ret = it.next(res);
next(ret)
}
function onRejected(err){
let ret = it.throw(err)
next(ret)
}
function next(ret){
if(ret.done) return resolve(ret.value);
return ret.value.then(onFufilled, onRejected)
}
})
}
let itResult = co(function *foo(){
let a = yield new Promise((resolve,reject)=>resolve(1))
console.log(a)
let b = yield new Promise((resolve,reject)=>resolve(2))
console.log(b)
})
itResult.then(res=>console.log(res))
/*
1
2
undefined
*/
简单实现
基于 Facebook 的 工具实现简易版生成器模型。
function foo(){
function nextState(v){
switch(state){
case 0:
state++;
return 42;
case 1:
state++;
x = v;
console.log(x);
return undefined;
}
}
var state = 0, x;
return {
next: function(v){
var ret = nextState(v);
return { value: ret, done: (state == 2)}
}
}
}
var it = foo();
it.next() // {value: 42, done: false}
it.next(10) //10
// {value: undefined, done: true}
参考资料
- 你不知道的《javascript》
- MDN
- co 库