深入了解 ES6 生成器

假如你对ES6生成器依旧感到陌生,推荐先阅读ES6生成器基础,但是如果你已经准备好了,那现在就来深入了解一下ES6生成器吧。

错误处理

ES6生成器设计中最厉害的一点就是,即使外部迭代控制器的进程是异步的,生成器内部的语义都是同步的

神奇的是,你可以利用这一点来实现简单的错误处理,可能你对此并不陌生—try…catch机制,比如:

即使上述代码暂停在yield 3这句,如果有error产生并被发送回了生成器,try…catch依然会捕捉到这个错误!你可以尝试用普通的异步方法(比如回调)来实现这个效果:)

但是,到底错误是如何被发送回生成器的呢?

你可以看到迭代器里我们使用了一个throw(..)方法,当生成器暂停在某一个yield中时,如果有一个错误在这时候发生了,这个方法就能将错误传给生成器。try…catch捕获错误的方式正如你所想的那样。

注意:如果你只使用了throw(..)尝试将错误传入生成器,但没有使用try…catch捕捉到错误,这个错误将会立马溜走(就像普通流程中的那样,如果没有捕获到错误那最终会变成未处理的拒绝),所以:

很明显,将错误处理(error handling)反过来写也是可以的:

委托生成器

可能你会想在一个生成器方法中调用别的生成器,在这里我说的并不仅仅是用普通的方式实例化一个生成器,而是将你自己的迭代器委托给其他的生成器。我们可以用yield的变形yield *来实现。

例子:

正如在这篇中(使用了function *foo(){}而不是用function* foo(){})说到的,我也会使用yield *foo()而非yield* foo(),这么写有助于对后文的理解。

让我们来分析下它的原理。可以看出,for…of循环中隐性地调用了next()yield 1yield 2将它们的值直接传到了被调用的next()中。

但接着执行到yield*时,你会注意到我们会调用另一个生成器(实例化foo()),可能最精准的对这个行为的定义就是,迭代器被委托到另一个生成器上。

yield*将委托(暂时地)从*bar()转移到*foo()后,for…of循环中的next()控制的就是foo(),因此yield 3yield 4将他们的值传回for..of循环。

*foo()调用结束后,控制器能够回到原来的生成器中,然后调用yield 5

简单起见,这个例子仅仅是将值的控制权进行转移。如果你没有使用for…of循环,而是手动地调用迭代器的next(…)然后传递信息进去,那些信息将会按照相同的方式通过yield*的委托:

yield*还有一个小把戏,它可以接收被委托的生成器的返回值:

正如你所看到的,yield *foo()被委托成为迭代控制器(next()调用),只要它被执行了,任何foo()的返回值(在这个例子中,返回值就是”foo”)会被传入yield*语句中作为结果值,然后赋值给了变量v

yieldyield*之间有一个有意思的区别:在yield中,无论结果是什么都会被传到后续的next()中;但是在yield*中,它只会从被委托的生成器的返回值里得到它的结果(因为next(..)清楚地通过委托将值传递过来)。

你也可以正向或者反向地使用yield*委托来处理错误:

如你所见,throw(“Uh oh!”)语句通过yield*委托到*foo()中的try…catch,然后抛出了错误。相似的,*foo()中的throw “Oops!”将错误抛回*bar(),这个错误又会在之后被另一个try…catch()捕获。如果我们没有捕获到其中任何一个错误,如你所想,它们都会继续传递。

总结

生成器可以同步执行语义,这句话的意思就是你可以通过yield语句使用try…catch错误处理机制。生成器的迭代器也有throw(..)方法,在生成器暂停的时候,将异常抛给它,这样try…catch自然可以在生成器内部捕获错误。

yield*允许你将迭代控制从当前的生成器委托到另一个生成器上。结果就是yield*表现的像生成器间的通道似的,可以传递信息和错误。

但是有一个根本性的问题到现在都还没有被问到:目前为止我们在这两篇文章里所谈到都是同步生成器的控制器方法,但生成器在异步代码模式中能提供什么帮助?

回答这个问题的关键在于,要在生成器暂停去开始一个异步任务然后在异步任务结束的时候返回的情况下(通过它的迭代器调用next())建立一种机制。在下一篇文章中我们会探讨用多种方式建立这样的生成器异步控制,所以请保持关注!

收藏 评论

关于作者:kmokidd

简介还没来得及写 :) 个人主页 · 我的文章 · 14

相关文章

可能感兴趣的话题



直接登录
跳到底部
返回顶部