es6的generator和nodejs的下一代框架koa
发布于 8 年前 作者 yinsigan 3833 次浏览 来自 分享

1. generator

es6有一个新功能特性,叫generator,它是解决回调地狱的一个生成器。

众所周知,nodejs的一大特性就是可以利用异步回调,它是优点,但也带来了回调噩梦。

有很多方法可以解决和减少回调嵌套太深的问题,比如promise, async,但都解决得不太彻底。

generator才是真正解决这类问题的利器,而本节所讲的koa框架也是基于generator。

要写一个generator函数很简单,我们先来看一个简单的函数。

var hello = function(name) {
  return 'hello ' + name;
}

console.log(hello('James'));

使用node --harmony example.js执行这个脚本。

将输出:

hello James

要改写成generator,很简单,只需要加一个*,如下:

var hello = function *(name) {
  return 'hello ' + name;
}

console.log(hello('James'));

终端将输出:

{}

现在还不行,再来改写一下。我们使用了next关键字。

var hello = function *(name) {
  return 'hello ' + name;
}

var gen = hello('James');
console.log(gen.next());

这个终端输出了我们要的效果:

{ value: 'hello James', done: true }

之前返回的是空对象{},现在有值了。

value代表返回的值,done代表这个generator是不是已经完成终止了。

什么意思?generator还有状态?

有的,它可以被暂停,重新运行,可以终止。

我们来试下它是如何被暂停的,这个时候得用yield关键字。

var hello = function *(name) {
  yield 'my name is ' + name;
  return 'hello ' + name;
}

var gen = hello('James');
console.log(gen.next());

终端输出:

{ value: 'my name is James', done: false }

done: false表示还没真正终止,停在了yield关键字所在的那一行。

要让它真正终止,得再调用一次next命令。

var hello = function *(name) {
  yield 'my name is ' + name;
  return 'hello ' + name;
}

var gen = hello('James');
console.log(gen.next());
console.log(gen.next());

输出:

{ value: 'my name is James', done: false }
{ value: 'hello James', done: true }

要调用两次next程序才最终停止。

下面给一个更直观的例子让你感受一下generator。

// 代码来源于https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function*
function* idMaker(){
  var index = 0;
  while(index < 3)
    yield index++;
}

var gen = idMaker();

console.log(gen.next().value); // 0
console.log(gen.next().value); // 1
console.log(gen.next().value); // 2
console.log(gen.next().value); // undefined

说了这么多,你可能会觉得,这个有什么用啊,能解决异步回调的问题?

能的,它的神奇在于yield那个命令,你可以写很多行这样的命令,后面接的是异步任务。

比如:

function* gen(){
  var result;
  result = yield fetch('a');
  console.log(result);
  result = yield fetch('b');
  console.log(result);
  result = yield fetch('c');
  console.log(result);
}

2. koa

next generation web framework for node.js

koa是下一代的nodejs web框架。以前比较流行的是express框架,现在好多人研究这个,express的主要开发者现在都转向于koa的开发了。

本节所讲的koa是第一版本的,如果要使用第二版本的,只是语法变了,思想却是不变的

我们先来感受一下koa。

先来安装。

$ npm install koa

创建app.js文件,内容如下:

var koa = require('koa');
var app = koa();

app.use(function *(){
  this.body = 'Hello World';
});

app.listen(3000);

使用node app.js运行这个文件,浏览器将输出Hello World

这个没什么,只是输出一行简单的文本。

我们再来看一个例子,是官方列出的。

// 代码来源于https://code.tutsplus.com/tutorials/introduction-to-generators-koajs-part-1--cms-21615
var koa = require('koa')();

koa.use(function* (next) {
  //do something before yielding/passing to next generator function in line which will be 1st event in downstream
  console.log("A");
  yield next;

  // do something when the execution returns upstream, this will be last event in upstream
  console.log("B");
});

koa.use(function* (next) {
  // do something before yielding/passing to the next generator function in line, this shall be 2nd event downstream
  console.log("C");

  yield next;

  // do something when the execution returns upstream and this would be 2nd event upstream
  console.log("D");
});

koa.use(function* () { // do something before yielding/passing to next generator function in line. Here it would be last function downstream
  console.log("E");
  this.body = "hey guys";
  console.log("F"); // First event of upstream (from the last to first)

});

koa.listen(3000);

它将输出:

A
C
E
F
D
B

为什么会是这样呢,这跟generator有关。

要理解这个,先来理解一下koa的特性。

开发koa,你会觉得你在写middleware,这个东西有点像ruby的rack middleware

它的中文名可以译成中间件

ruby的中间件是这样的,它传入一个环境变量(env),经过中间件的处理,返回一个数组,数组内容是[status, headers, body]。

分别是状态码,响应头信息,内容体。

一个中间件处理完之后,并不直接返回,而是把它的结果交给下一个中间件,直接全部处理结束,才返回。

它有点像一坐桥,连接桥头和桥尾两端,然后像koa应用是由很多这样的桥组成。

它是用中间件来组合而成一个应用。

koa.use里面的就可以理解为一个中间件。

这样有什么好处呢?

好处多着呢,比如可以统一处理一些事情,比如cache,日志输出,不然没有中间件,你要处理这样的东西,或许只能在应用中,一行行地改,而有了中间件,它在中间拦截处理了,很方便,特别有意思的是,koa应用可以作为一个中间件应用包在一个express应用中,要开发koa的中间件也很简单,使用别人开发好的,一拿来就能用,超级方便。

现在回头来看上面那个代码,为什么是那样输出呢?

你得理解yield这个指令,它会暂停你的中间件,会执行权交给下一个中间件,等下个中间件执行完,才会最终执行yield之后的命令。所以最后输出的是B

koa之路还很长,好好研究吧。

完结。

2 回复

嗯, 楼主再深入研究研究, 学习的精神总是值得肯定的!

最后一段话 是next的功能 不是yield

回到顶部