打破完整运行
JavaScript 开发者在代码中几乎普遍依赖的一个假定:一个函数 一旦开始执行,就会运行到结束,期间不会有其他代码能够打断它并插入其间。
js
let x = 1
function foo() {
x++
bar() // <-- 这一行是什么作用? 234
console.log('x:', x)
}
function bar() {
x++
}
foo() // x: 3
// 构造一个迭代器it来控制这个生成器
const it = foo()
// 这里启动foo()!
it.next()
x // 2 bar();
x // 3 it.next(); // x: 3输入和输出
js
function* foo(x) {
const y = x * (yield)
return y
}
const it = foo(3)
// 启动foo(..)
it.next() // { value: undefined, done: false }
// 把值 7 传回作为被暂停的 yield 表达式的结果
// 相当于把第一个 yield 替换成 7
it.next(7) // { value: 21, done: true }多个迭代器
每次构建一个迭代器,实际上就隐式构建了生 成器的一个实例,通过这个迭代器来控制的是这个生成器实例。
js
function* foo() {
const x = yield 2
z++
const y = yield x * z
console.log(x, y, z)
}
var z = 1
const it1 = foo()
const it2 = foo()
let val1 = it1.next().value // 2 <-- yield 2
let val2 = it2.next().value // 2 <-- yield 2
val1 = it1.next(val2 * 10).value // 40 <-- x:20, z:2
val2 = it2.next(val1 * 5).value // 600 <-- x:200, z:3
it1.next(val2 / 2) // y:300
// 20 300 3
it2.next(val1 / 4) // y:10
// 200 10 3生成器产生值
生产者与迭代器
假定你要产生一系列值,其中每个值都与前面一个有特定的关系。
使用闭包实现
js
const gimmeSomething = (function () {
let nextVal
return function () {
if (nextVal === undefined) {
nextVal = 1
}
else {
nextVal = 3 * nextVal + 6
}
return nextVal
}
})()
gimmeSomething() // 1
gimmeSomething() // 9
gimmeSomething() // 33
gimmeSomething() // 105使用迭代器实现
js
var something = (function() {
var nextVal;
return {
// for..of循环需要
[Symbol.iterator]: function() {
return this;
},
// 标准迭代器接口方法
next: function() {
if (nextVal === undefined) {
nextVal = 1;
} else {
nextVal = 3 * nextVal + 6;
}
return { done: false, value: nextVal };
},
};
})();
something.next().value; // 1
something.next().value; // 9
something.next().value; // 33
something.next().value; // 105
function* something() {
var nextVal;
while (true) {
if (nextVal === undefined) {
nextVal = 1;
} else {
nextVal = 3 * nextVal + 6;
}
yield nextVal;
}
}ES6 还新增了一个 for..of 循环,这意味着可以通过原生循环语法自动迭代标准迭代器:
js
for (const v of something) {
console.log(v)
// 不要死循环!
if (v > 500) {
break
}
}
// 1 9 33 105 321 969
// 以手工在迭代器上循环
for (var ret; (ret = something.next()) && !ret.done;) {
console.log(ret.value)
// 不要死循环!
if (ret.value > 500) {
break
}
}
// 1 9 33 105 321 969for..of 循环在每次迭代中自动调用 next(),它不会向 next() 传入任何值,并且会在接收 到 done:true 之后自动停止
生成器委托
从一个生成器调用另一个生成器。yield 委托的主要目的是代码组织,以达到与普通函数调用的对称。
js
function* foo() {
console.log('*foo() starting')
yield 3
yield 4
console.log('*foo() finished')
}
function* bar() {
// yield *[1,2] 会消耗数组值 [1,2] 的默认迭代器。
yield * [1, 2]
yield * foo() // yield委托!
yield 5
}
const it = bar()
it.next().value // 1
it.next().value // 2
it.next().value // *foo()启动
// 3
it.next().value // 4
it.next().value // *foo()完成
// 5yield * 把迭代器实例控制(当前 *bar() 生成器的)委托给 / 转移到了这另一个 *foo() 迭代器。
消息委托
思考下面消息的消息流出入
js
function* foo() {
// it.next(2)
console.log('inside *foo():', yield 'B')
// it.next(3)
console.log('inside *foo():', yield 'C')
return 'D'
}
function* bar() {
// it.next(1)
console.log('inside *bar():', yield 'A')
// yield委托!
// *bar() 中return "D"
console.log('inside *bar():', yield * foo())
// it.next(4)
console.log('inside *bar():', yield 'E')
return 'F'
}
const it = bar()
console.log(it.next().value)
// outside: A
console.log(it.next(1).value)
// inside *bar(): 1
// outside: B
console.log(it.next(2).value)
// inside *foo(): 2
// outside: C
console.log(it.next(3).value)
// inside *foo(): 3
// inside *bar(): D
// outside: E
console.log(it.next(4).value)
// inside *bar(): 4
// outside: F异常也被委托
js
function* foo() {
try {
yield 'B'
}
catch (err) {
// 捕获 it.throw(2) 中传进来的值
console.log('error caught inside *foo():', err)
}
yield 'C'
throw 'D'
}
function* bar() {
yield 'A'
try {
yield * foo()
}
catch (err) {
// 捕获 *foo() 中 throw "D"
console.log('error caught inside *bar():', err)
}
yield 'E'
yield * baz()
// 注:不会到达这里!
// 从 *baz() throw 出来的异常并没有在 *bar() 内被捕获——所以 *baz() 和 *bar()
// 都被设置为完成状态。这段代码之后,就再也无法通过任何后续的 next(..) 调用得到
// 值 "G",next(..) 调用只会给 value 返回 undefined。
yield 'G'
}
function* baz() {
throw 'F'
}
const it = bar()
console.log('outside:', it.next().value)
// outside: A
console.log('outside:', it.next(1).value)
// outside: B
console.log('outside:', it.throw(2).value)
// error caught inside *foo(): 2
// outside: C
console.log('outside:', it.next(3).value)
// error caught inside *bar(): D
// outside: E
try {
console.log('outside:', it.next(4).value)
}
catch (err) {
console.log('error caught outside:', err)
}
// error caught outside: F
console.log('it.next().value: ', it.next().value)异步委托
js
function* foo() {
const r2 = yield request('http://some.url.2')
const r3 = yield request(`http://some.url.3/?v=${r2}`)
return r3
}
function* bar() {
const r1 = yield request('http://some.url.1')
const r3 = yield * foo()
console.log(r3)
}
run(bar)递归委托
js
function* foo(val) {
if (val > 1) {
// 生成器递归
val = yield * foo(val - 1)
}
return yield request(`http://some.url/?v=${val}`)
}
function* bar() {
const r1 = yield * foo(3)
console.log(r1)
}
run(bar)