yield next和yield* next的区别
yield next和yield* next之间到底有什么区别?为什么需要yield* next?经常会有人提出这个问题。虽然我们在代码中会尽量避免使用yield* next以减少新用户的疑惑,但还是经常会有人问到这个问题。为了体现自由,我们在koa框架内部使用了yield* next,但是为了避免引起混乱我们并不提倡这样做。
相关文档,可以查看这里的说明harmony proposal.
yield委托(delegating)做了什么?
假设有下面两个generator函数:
- function* outer() {
- yield 'open'
- yield inner()
- yield 'close'
- }
- function* inner() {
- yield 'hello!'
- }
通过调用函数outer()能产出哪些值呢?
- var gen = outer()
- gen.next() // -> 'open'
- gen.next() // -> a generator
- gen.next() // -> 'close'
但如果我们把其中的yield inner()改成yield* inner(),结果又会是什么呢?
- var gen = outer()
- gen.next() // -> 'open'
- gen.next() // -> 'hello!'
- gen.next() // -> 'close'
事实上,下面两个function本质上来说是等价的:
- function* outer() {
- yield 'open'
- yield* inner()
- yield 'close'
- }
- function* outer() {
- yield 'open'
- yield 'hello!'
- yield 'close'
- }
从这个意义上来说,委托的generator函数替代了yield*关键字的作用!
这与Co或Koa有什么关系呢?
Generator函数已经很让人抓狂了,它并不能帮助Koa的generator函数使用Co来控制流程。很多人都会被本地的generator函数和Co框架提供的功能搞晕。
假设有以下generator函数:
- function* outer() {
- this.body = yield inner
- }
- function* inner() {
- yield setImmediate
- return 1
- }
如果使用Co,它实际上等价于下面的代码:
- function* outer() {
- this.body = yield co(function inner() {
- yield setImmediate
- return 1
- })
- }
但是如果我们使用yield委托,完全可以去掉Co的调用:
- function* outer() {
- this.body = yield* inner()
- }
那么最终执行的代码会变成下面这样:
- function* outer() {
- yield setImmediate
- this.body = 1
- }
每一个Co的调用都是一个闭包,因此它会或多或少地存在一些性能上的开销。不过你也不用太担心,这个开销不会很大,但是如果使用委托yield,我们就可以降低对第三方库的依赖而从代码级别避免这种开销。
有多快?
这里有一个链接,是之前我们讨论过的有关该话题的内容:https://github.com/koajs/compose/issues/2. 你不会看到有太多的性能差异(至少在我们看来),特别是因为实际项目中的代码会显著地降低这些基准。因此,我们并不提倡使用yield* next,不过在内部使用也并没有坏处。
有趣的是,通过使用yield* next,Koa比Express要快!Koa没有使用dispatcher(调度程序),这与Express不同。Express使用了许多的调度程序,如connect, router等。
使用委托yield,Koa事实上将:
- co(function* () {
- var start = Date.getTime()
- this.set('X-Powered-By', 'koa')
- if (this.path === '/204')
- this.status = 204
- if (!this.status) {
- this.status = 404
- this.body = 'Page Not Found'
- }
- this.set('X-Response-Time', Date.getTime() - start)
- }).call(new Context(req, res))
拆解成:
- app.use(function* responseTime(next) {
- var start = Date.getTime()
- yield* next
- this.set('X-Response-Time', Date.getTime() - start)
- })
- app.use(function* poweredBy(next) {
- this.set('X-Powered-By', 'koa')
- yield* next
- })
- app.use(function* pageNotFound(next) {
- yield* next
- if (!this.status) {
- this.status = 404
- this.body = 'Page Not Found'
- }
- })
- app.use(function* (next) {
- if (this.path === '/204')
- this.status = 204
- })
一个理想的Web application看起来就和上面这段代码差不多。在上面使用Co的那段代码中,唯一的开销就是启动单个co实例和我们自己的Context构造函数,方便起见,我们将node的req和res对象封装进去了。
使用它进行类型检查
如果将yield*应用到不是generator的函数上,你将会看到下面的错误:
- TypeError: Object function noop(done) {
- done();
- } has no method 'next'
这是因为基本上任何具有next方法的东西都被认为是一个generator函数!就我个人而言,我很喜欢这个,因为默认我会假设我就是一个yield* gen()。我重写了很多我的代码来使用generator函数,如果我看到某些东西没有被写成generator函数,那么我会想,我可以将它们转换成generator函数而使代码更简化吗?
当然,这也许并不适用于所有人,你也许能找到其它你想要进行类型检查的原因。
上下文
Co会在相同的上下文中调用所有连续的可yield的代码。如果你想在yield function中改变调用的上下文,会有些麻烦。看下面的代码:
- function Thing() {
- this.name = 'thing'
- }
- Thing.prototype.print = function (done) {
- var self = this
- setImmediate(function () {
- console.log(self.name)
- })
- }
- // in koa
- app.use(function* () {
- var thing = new Thing()
- this.body = yield thing.print
- })
这里你会发现this.body是undefined,这是因为Co事实上做了下面的事情:
- app.use(function* () {
- var thing = new Thing()
- this.body = yield function (done) {
- thing.print.call(this, done)
- }
- })
而这里的this指向的是Koa的上下文,而不是thing。
上下文在JavaScript中很重要,在使用yield*时,可以考虑使用下面两种方法之一:
- yield* context.generatorFunction()
- yield context.function.bind(context)
这样可以让你避开99%的generator函数的上下文问题,从而避免了使用yield context.method给你带来的困扰!
yield next和yield* next的区别的更多相关文章
- sleep、yield、wait、join的区别(阿里面试)
1. Thread.sleep(long) 和Thread.yield()都是Thread类的静态方法,在调用的时候都是Thread.sleep(long)/Thread.yield()的方式进行调 ...
- sleep、yield、wait、join的区别(阿里)
只有runnable到running时才会占用cpu时间片,其他都会出让cpu时间片.线程的资源有不少,但应该包含CPU资源和锁资源这两类.sleep(long mills):让出CPU资源,但是不会 ...
- Enumerator yielder.yield 与 Proc.yield 区别
最近看ruby cookbook遇到这个用法,google一下,这里原文解释 http://stackoverflow.com/questions/18865860/enumerator-yielde ...
- 深入理解yield(三):yield与基于Tornado的异步回调
转自:http://beginman.cn/python/2015/04/06/yield-via-Tornado/ 作者:BeginMan 版权声明:本文版权归作者所有,欢迎转载,但未经作者同意必须 ...
- 12.C#yield return和yield break及实际应用小例(六章6.2-6.4)
晚上好,各位.今天结合书中所讲和MSDN所查,聊下yield关键字,它是我们简化迭代器的关键. 如果你在语句中使用了yield关键字,则意味着它在其中出现的方法.运算符或get访问器是迭代器,通过使用 ...
- yield return 和 yield break
//yield return 返回类型必须为 IEnumerable.IEnumerable<T>.IEnumerator 或 IEnumerator<T>. static I ...
- C#yield return和yield break
C#yield return和yield break 晚上好,各位.今天结合书中所讲和MSDN所查,聊下yield关键字,它是我们简化迭代器的关键. 如果你在语句中使用了yield关键字,则意味着它在 ...
- 线程之sleep(),wait(),yield(),join()等等的方法的区别
操作线程的常用方法大体上有sleep(),join(),yield()(让位),wait(),notify(),notifyAll(),关键字synchronized等等. 由于这些方法功能有些 ...
- 线程中的sleep()、join()、yield()方法有什么区别?
sleep().join().yield()有什么区别? sleep() sleep() 方法需要指定等待的时间,它可以让当前正在执行的线程在指定的时间内暂停执行,进入阻塞状态,该方法既可以让其他同优 ...
随机推荐
- java微信开发API解析(二)-获取消息和回复消息
java微信开发API解析(二)-获取消息和回复消息 说明 * 本演示样例依据微信开发文档:http://mp.weixin.qq.com/wiki/home/index.html最新版(4/3/20 ...
- @Autowired注解在抽象类中实效的原因分析
最近在工作中遇到这个问题,在抽象类中使用Autowired这个注解,注入mybatis的dao时,总是出现空指针异常,通过日志的打印,发现是这个dao注入失败为空.然后通过new出spring上下文对 ...
- HBase shell 命令介绍
HBase shell是HBase的一套命令行工具,类似传统数据中的sql概念,可以使用shell命令来查询HBase中数据的详细情况.安装完HBase之后,如果配置了HBase的环境变量,只要在sh ...
- 为 Azure IoT Edge 设备部署 Azure Stream Analytics 服务
在前面的两篇文章<Azure IoT Edge on Windows 10 IoT Core>和<Azure IoT Edge on Raspberry Pi 3 with Rasp ...
- 神经网络NN笔记
参考:http://www.cnblogs.com/subconscious/p/5058741.html 俗话说,好记性不如烂笔头~~~~ 边学边记,方便以后查找~~~~~ 一.介绍一下经典的神经网 ...
- [LeetCode] 二叉树相关题目(不完全)
最近在做LeetCode上面有关二叉树的题目,这篇博客仅用来记录这些题目的代码. 二叉树的题目,一般都是利用递归来解决的,因此这一类题目对理解递归很有帮助. 1.Symmetric Tree(http ...
- 基于CDH 5.9.1 搭建 Hive on Spark 及相关配置和调优
Hive默认使用的计算框架是MapReduce,在我们使用Hive的时候通过写SQL语句,Hive会自动将SQL语句转化成MapReduce作业去执行,但是MapReduce的执行速度远差与Spark ...
- 推荐:让你快速搞定各服务端(api,pc,mobile,wechat)代码
如果你在写服务端 (PHP) ,会因为项目须求(做app.pc.mobiel.微信) 而写几套代码的,你不觉得很累吗? 现在的很多开源框架商用版本在做程序方面都是这么一套一套的,维护起来,二开起来特别 ...
- 常规流(Normal flow)
连我自己把float和绝对定位,都称为脱离文档流,想想概念又不那么清晰,于是寻找了W3C资料来理解,才发觉不应该叫文档流. 资料 英文:https://www.w3.org/TR/CSS22/visu ...
- bzoj 2727: [HNOI2012]双十字
Description 在C 部落,双十字是非常重要的一个部落标志.所谓双十字,如下面两个例子,由两条水平的和一条竖直的"1"线段组成,要求满足以下几个限制: 我们可以找到 5 个 ...