javascript与生成器交互
使用yield表达式从生成器中返回多个值,但生成器远比这更加强大!我们还能向生成器发送值,从而实现双向通信!使用生成器我们能够生成中间结果,在生成器以外我们也能够使用该结果进行任何操作,然后,一旦准备好了,就能够把整个新计算得到的数据再完完整整返回给生成器。
作为生成器函数发送值
向生成器发送值的最简单方法如其他函数一样,调用函数并传入实参。
console.log('--------------向生成器发送数据及从生成器接收数据------------');
//生成器可以像其他函数一样接收标准参数
function* NinjaGenerator(action) {
//奇迹出现了,产生一个值的同时,生成器会返回一个中间计算结果。通过带有参数的调用迭代器的next方法,我们可以将数据传递回生成器。
const imposter = yield ('Hattori ' + action);
//传递回的值将成为yield表达式的返回值,因此imposer的值是Hanzo
if (imposter === 'Hanzo') {
console.log('The generator has been infiltrated!');
yield ("Yoshi (" + imposter + ") " + action);
}
}
//普通的参数传递
//触发生成器的执行,并检查返回值是否正确
const ninjaIterator = NinjaGenerator('skulk');
//触发生成器执行,并检测返回值是否正确
const result1 = ninjaIterator.next();
if (result1.value === 'Hattori skulk') {
console.log('Hattori is skulking!');
}
//将数据作为next方法的参数传递给生成器,并检测返回值是否符合预期值。
const result2 = ninjaIterator.next('Hanzo');
if (result2.value === 'Yoshi (Hanzo) skulk') {
console.log('We have an imposter!');
}
使用next方法向生成器发送值
除了在第一次调用生成器的时候向生成器提供数据,我们还能通过next方法向生成器传入参数。在这个过程中,我们把生成器函数从挂起状态恢复到执行状态。在当前挂起的生成器中,生成器把这个传入的值用于整个yeild表达式。
在上述代码中,我们调用了两次ninjaIterator的next方法。第一次调用ninjaIterator.next(),请求了生成器的第一个值。由于生成器还没开始执行,这次调用则启动了生成器,对表达式“Hattori ” + action进行求值,得到了值"Hattori skulk",并将该生成器的执行挂起。
首次调用ninjaIterator.next()方法向生成器请求一个新值,在yield表达式的位置返回了Hattori skulk,并挂起执行。第二次调用ninjaIterator.next('Hanzo')又请求一个新值,但它同升湖向生成器发送了实参Hanzo。这个只会在整个yield表达式中使用,同时,imposter变量也就包含了字符Hanzo
然而第二次调用ninjaIterator的next方法则发生了有趣的事:ninjaIterator.next('Hanzo')。这一次,我们使用next方法将计算得到的值又传递会生成器。生成器函数耐心等待着,在表达式yield('Hattoti' + action)位置挂起,故而值Hanzo作为参数传入了next()方法,并用作整个yield表达式的值。上述代码中,也就是表示语句imposter = yield ('Hattoti' + action)中的变量imposter会以值Hazo作为结尾。
以上展示了如何在生成器中双向通信。我们通过yield语句从生成器中返回值,再使用迭代器的next()方法把值传递会生成器。
注意:
next方法为等待中的yield表达式提供了值,所以,如果没有等待中的yield表达式,也就没有什么值能应用的。基于这个基因,我们无法通过第一次调用next方法来向生成器提供该值。但记住,如果需要为生成器提供一个初始值,你可以调用生成器自身,就像NinjaGenerator('skulk')。
抛出异常
还有一种稍微不那么正统的方式将值应用到生成器上:通过抛出一个异常。每个迭代器除了有一个next方法,还抛出一个方法。
console.log("--------------------向生成器抛出异常--------------------");
function* NinjaGenerator() {
try{
yield 'Hattori';
console.log("The expected exception did not occur!");
} catch (e) {
console.log("e:" + JSON.stringify(e));
if (e === 'Catch this!') {
console.log("Aha! We caught an exception!");
}
}
}
const ninjaIterator = NinjaGenerator();
//从生成器拉取一个值
const result1 = ninjaIterator.next();
if (result1.value === 'Hattori') {
console.log("We got Hattori!");
}
//向生成器抛出一个异常
ninjaIterator.throw("Catch this!");
我们把整体函数体用一个try-catch块包裹起来了。
function* NinjaGenerator() {
try{
yield 'Hattori';
console.log("The expected exception did not occur!");
} catch (e) {
console.log("e:" + JSON.stringify(e));
if (e === 'Catch this!') {
console.log("Aha! We caught an exception!");
}
}
}
通过创建一个迭代器继续执行,然后从生成器中获取一个值:
const ninjaIterator = NinjaGenerator();
//从生成器拉取一个值
const result1 = ninjaIterator.next();
最后,通过使用在所有迭代器上都有效的throw方法,我们可以向生成器抛出一个异常:
ninjaIterator.throw("Catch this!");
这个能让我们把异常抛回生成器的特性初看可能有点奇怪。为什么要进行这样的操作呢?我们将使用这个特性来改善异步服务器端的通信。
参考《JavaScript忍者秘籍》