ES6-深入理解Generator yield & Koa中间件执行顺序
来源:互联网 发布:数据超市 编辑:程序博客网 时间:2024/05/22 14:14
几个月前写过一篇博客,讲Generator,比较基础。最近总在写ES6,想深入讲讲yield的执行顺序。你可能想问,Generator执行顺序很简单啊,就是调用next()就执行下一个yield后面的代码。很多问题,如果你认为很简单,很可能是你理解不深刻,就像我当初也认为Generator很简单。如果你关心koa中间件的执行顺序也可以接着看看。
yield与yield*
关于这个话题,你只需要知道四点:
- yield* fn():相当于用fn的内容来替换该位置,不会消耗一次next()调用,fn内的代码会被执行
- yield* fn:报错。因为fn是一个generator function,而yield*后面应该是一个generator
- yield fn():yield的结果是一个generator,消耗一次gen的next()调用,且fn内的代码不会被执行
- yield fn: yield的结果是一个generator function,消耗一次gen的next()调用,且fn内的代码不会被执行
例子:
class Test{ constructor(){ this.gen = this.f1(); this.gen.next(); this.gen.next(); } *noop() {} *f1() { console.log('1: pre'); yield this.f2(); console.log('1: after'); } *f2() { console.log('2: pre'); yield this.f3(); console.log('2: after'); } *f3() { console.log('3: pre'); console.log('3: after'); }}let test = new Test();
打印出:
1: pre1: after
没有执行f2,f3代码,因为yield不会继续执行后面的函数,但是yield* 会继续执行后面的函数,知道得出一个结果。把上面代码的yield
换成yield*
,执行结果:
1: pre2: pre3: pre3: after2: after1: after
如果你知道Koa,你会发现,这就是Koa中间件的执行顺序,但是Koa不是通过yield*实现Generator的自动执行,而是通过co。
Koa中间件执行顺序的原理
下面是一段koa代码,如果你不知道koa也没关系,只要知道koa的所有中间件都是Generator函数:
var koa = require('koa');var app = koa();app.use(function* f1(next) { console.log('f1: pre next'); yield next; console.log('f1: post next');});app.use(function* f2(next) { console.log(' f2: pre next'); yield next; console.log(' f2: post next');});app.use(function* f3(next) { console.log(' f3: pre next'); this.body = 'hello world'; console.log(' f3: post next');});app.listen(4000);
你感觉执行顺序是什么?
答案:
f1: pre next f2: pre next f3: pre next f3: post next f2: post nextf1: post next
执行顺序:
并不是执行完f1再执行f2,最后执行f3。
似乎和上面yield*执行顺序有点相似呢。
compose
上述带yield*的函数为了实现这个“回形”执行顺序时候,在定义f1的时候需要yield* f2()
,显然Koa不可能在注入每个中间件时候再改变其内部yield的内容,那么我们就需要compose函数了。
function compose(middleware) { return function*(next) { var i = middleware.length; var prev = next || noop(); var curr; while (i--) { curr = middleware[i]; prev = curr.call(this, prev); } yield* prev; }}function* noop() {}
调用方式
let gen = compose([f1, f2, f3]); //f1,f2,f3是带yield的Generator Function//gen是一个类似 f1(f2(f3({})))的Generator对象,Generator对象是调用一次Generator Function返回的结果
co
如果你执行gen.next()
会返回11: prev
,不能调用f2和f3,当然,如果你和上面一样,把yield都改成yield* 是会链式调用f2,f3的。可是koa没有规定必须使用yield*。为了执行gen,koa引入了co——一个Gnerator自动执行函数。
const co = require ('co');co(gen);
f1, f2,f3就可以实现上述“回形”执行了。如果你想深入co原理,请戳co。
Generator的GeneratorStatus
更深入一点,如果有多个yield会怎么样。
class Test{ constructor(){ this.g = this.compose([this.f1, this.f2, this.f3])(); co(this.g); } *noop() {} *f1(next) { console.log('11: prev'); yield next; console.log('11: after'); console.log('12: prev'); yield next; console.log('12: after'); } *f2(next) { console.log('21: prev'); yield next; console.log('21: after'); console.log('22: prev'); yield next; console.log('22: after'); } *f3(next) { console.log('31: prev'); yield next; console.log('31: after'); console.log('32: prev'); yield next; console.log('32: after'); } compose(middleware) { let self = this; return function*(next) { var i = middleware.length; var prev = next || self.noop(); var curr; while (i--) { curr = middleware[i]; prev = curr.call(this, prev); } yield* prev; } } } let test = new Test();
会不会 f1 -> f2 -> f3 -> f2 遇到f2的第二个yield又去执行f3?
答案是不会的。遇到f2第二个yield会跳过,继续执行下面语句,不会再执行一遍f3。原因和简单,Generator对象是有状态的,即GeneratorStatus属性,其值从suspended变为closed后,不会再改变。就是说,Generator对象在一个环境中,只能执行一遍。上述代码执行结果:
11: prev21: prev31: prev31: after32: prev32: after21: after22: prev22: after11: after12: prev12: after
当一个Generator函数没有未执行的yield,变为普通函数,GeneratorStatus的值从suspended变为closed。
- ES6-深入理解Generator yield & Koa中间件执行顺序
- koa中间件原理 && yield && generator
- ES6 generator 与 koa 中间件 是如何 generator解决异步的
- 深入理解python的yield和generator
- 深入解析 ES6:Generator
- 理解 ES6 Generator 函数
- 深入理解yield
- Python 深入理解yield
- Python 深入理解yield
- Python 深入理解yield
- Python 深入理解yield
- Python 深入理解yield
- 深入理解yield
- python---深入理解yield
- 深入理解yield
- [Django高级]理解django中的中间件机制和执行顺序
- 深入探析koa之中间件流程控制篇
- 深入理解Unity脚本的执行顺序
- 传球70分做法
- [SCOI2016][JZOJ4632]幸运数字
- Ubuntu安装JDK1.8
- HDU 4701 GAME
- jQuery(三)——包装集
- ES6-深入理解Generator yield & Koa中间件执行顺序
- 【一天一道LeetCode】#202. Happy Number
- DZ目录结构
- XML——笔记
- Android RxJava 初步接触二:from,just等方法的使用
- java的一些小技巧!
- 动态规划若干优化 & 集训部分总结
- live555详解
- 【一天一道LeetCode】#263. Ugly Number