深入浅出Koa常用工具分享 帮你降低编程难度

Posted CSDN

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了深入浅出Koa常用工具分享 帮你降低编程难度相关的知识,希望对你有一定的参考价值。

在前一篇 文章中,我们学习了很多关于生成器的知识,借助于生成器,我们可以在编写异步代码时使用同步代码风格, 这样做能够简化异步编程,因为同步的代码更加简单,优雅,也更可靠,而异步代码则可能会将我们引入回调地狱中。


在本文中我们会介绍一些能够降低编程难度的工具,因此会着重介绍其中最有趣的部分。此外, 本文还会介绍Koa的基本特性和运行机制。


简单回顾


[js]

  1. // First part

  2. var thunkify = require('thunkify');

  3. var fs = require('fs');

  4. var read = thunkify(fs.readFile);


  5. // Second part

  6. function *bar () {

  7. try {

  8. var x = yield read('input.txt');

  9. } catch (err) {

  10. console.log(err);

  11. }

  12. console.log(x);

  13. }


  14. // Third part

  15. var gen = bar();

  16. gen.next().value(function (err, data) {

  17. if (err) {

  18. gen.throw(err);

  19. }


  20. gen.next(data.toString());

  21. });


这是 中的最后一个例子,正如你看到的那样,我们可以将程序分为三个重要部分。 首先,我们需要创建了一个被thunk化的函数,我们将在生成器中使用到这个thunk函数。 然后,我们可以编写我们的生成器函数,并在函数中使用前面创建的thunk函数。 最后,我们调用并遍历生成器,并进行错误处理等工作。如果你仔细的考虑了这个过程, 你会发现最后一个部分和整个程序的核心内容并没有太大的关联。它只不过是用来让我们运行生成器而已。 因此,我们可以利用一个工具来简化这个过程,幸运的是,正好有一个这样的模块,它就是co。


co


是一个基于生成器的Node流程控制模块。下面的例子所做的工作与前一个例子完全一样, 但是我们没有直接编写调用生成器的代码(上面代码的第三部分)。取而代之的是,我们将生成器直接传递给了co函数, 然后立即调用这个函数,任务就这么神奇的完成了。其实,也没什么神奇的,co只不过是帮你完成了生成器调用代码而已, 因此我们没必要担心下层的工作。


[js]

  1. var co = require('co');

  2. var thunkify = require('thunkify');

  3. var fs = require('fs');


  4. var read = thunkify(fs.readFile);


  5. co(function *bar () {

  6. try {

  7. var x = yield read('input.txt');

  8. } catch (err) {

  9. console.log(err);

  10. }

  11. console.log(x);

  12. })();


正如你已经知道的那样,你可以在yield后面跟上任何你需要计算(用于获取某些值、执行某些任务)的表达式。因此, 不仅仅是thunk函数可以跟在yield后面。由于co主要用来创建更简单的控制流,因此它只能用来yield一些特定类型。 目前主要可以在co中被yield的类型有:


  • thunks(函数)

  • 数组(并行执行)

  • 对象(并行执行)

  • 生成器(代理)

  • 生成器函数(代理)

  • Promises


前面我们已经讨论了thunks是如何工作的,因此下面我们来讨论一些其他内容。


并行执行


[js]

  1. co(function *() {

  2. // 3 concurrent reads

  3. var reads = yield [

  4. read('input.txt', 'utf-8'),

  5. read('input.txt', 'utf-8'),

  6. read('input.txt', 'utf-8')

  7. ];

  8. console.log(reads);


  9. // 2 concurrent reads

  10. reads = yield {a: read('input.txt', 'utf-8'), b: read('input.txt', 'utf-8')};

  11. console.log(reads);

  12. }).catch(onerror);


如果你yield了一个数组或对象,它会并行的计算数组或对象中的内容。当然,如果集合中的内容为thunks或生成器, 它也能同样进行处理。并且你也可以进行嵌套,它能够便利你的数组或对象,然后并行的执行工作函数。需要记住的是: yield后的生成的结果并不是扁平的,而是保持和原来相同的结构。


[js]

  1. co(function *() {


  2. var a = [read('cinq.txt', 'utf-8'), read('deux.txt', 'utf-8')];

  3. var b = [read('six.txt', 'utf-8'), read('un.txt', 'utf-8')];


  4. var files = yield [a, b];

  5. console.log(files); // [ [ 'cinq', 'deux' ], [ 'six', 'un' ] ]


  6. }).catch(onerror);


你也可以在thunk调用后进行yield,同样能达到并行的目的。


[js]

  1. co(function *() {

  2. var a = read('input.txt', 'utf-8');

  3. var b = read('input.txt', 'utf-8');


  4. // 2 concurrent reads

  5. console.log([yield a, yield b]);


  6. // or


  7. // 2 concurrent reads

  8. console.log(yield [a, b]);

  9. }).catch(onerror);


委托


前面说过,你也可以在yield后面跟上生成器。注意你可以不必使用yield *。


[js]

  1. var stat = thunkify(fs.stat);


  2. function *size(file) {

  3. var s = yield stat(file);

  4. return s.size;

  5. }


  6. co(function *() {

  7. var f = yield size('input.txt'); // hello world

  8. console.log(f); // 11

  9. }).catch(onerror);


我们大致尝试用co处理了每一种可以被yield的类型。下面我们来看最后一个例子,算作总结。


[js]

  1. var co = require('co');

  2. var fs = require('fs');


  3. function size(file) {

  4. return function (fn) {

  5. fs.stat(file, function (err, stat) {

  6. if (err) return fn(err);

  7. fn(null, stat.size);

  8. });

  9. }

  10. }


  11. function *foo() {

  12. var a = yield size('un.txt');

  13. var b = yield size('deux.txt');

  14. var c = yield size('trois.txt');

  15. return [a, b, c];

  16. }


  17. function *bar() {

  18. var a = yield size('quatre.txt');

  19. var b = yield size('cinq.txt');

  20. var c = yield size('six.txt');

  21. return [a, b, c];

  22. }


  23. co(function *() {

  24. var results = yield [foo(), bar()];

  25. console.log(results);

  26. }).catch(onerror);


  27. function onerror(err) {

  28. console.error(err.stack);

  29. }


我想目前为止你应该能够对生成器有了一个较为清晰的认识了,并且能够借用co很好的处理异步控制流。关于co本身, 如果你想了解的更深入些,可以阅读它的源代码。现在,我们将重点移到Koa本身上来。


Koa


对于Koa而言,你需要知道的并没有很多。如果你阅读了它的源代码的话,你甚至会发现总共才4个文件,每个才300行左右。 Koa遵循了每个程序只做一件事并将其做的更好的原则。你会发现,每个优秀的Koa模块都非常的紧凑,并且只做一件事, 并且在其他模块的基础上进行构建。你应该记住这一点,并使用这个方法来开发你自己的Koa模块。这将会有益于所有人, 也有助于你和其他人阅读源代码。先记住这一点后,然后我们来看看Koa的一些核心特性。


应用 Application


[js]

  1. var koa = require('koa');

  2. var app = koa();


创建一个Koa应用只不过是在调用一个相关的模块函数而已。它会提供给你一个对象,这个对象包含一个生成器数组 (一组中间件),对收到的每个请求,它会使用一种堆栈式的方法执行。


级联 Cascading


当你使用Koa时,一个非常重要的术语是中间件。现在让我们来搞清楚这个概念。


  • Koa中的中间件是一组用于处理请求的函数。使用Koa创建的服务器,可以有一组堆栈结构的中间件与它关联。

级联在Koa中意味着:控制流会流经一组中间件。在web开发中这个概念非常的有用,你可以将复杂的行为借助于这个手段变得简单。 Koa通过生成器函数来实现这一点(中间件级联),并且更具创新性和简洁性。 它能够yiled下游中间件,之后控制流再返回到上游中间件。 将生成器加入到控制流中非常简单,只要在调用use()方法时使用生成器即可。 猜猜下面的代码为什么每次接收到请求时会输出的是A, B, C, D, E。


下面的代码演示了一个服务器,因此listen方法用于监听一个具体的端口:


[js]

  1. var koa = require('koa');

  2. var app = koa();


  3. // 1

  4. app.use(function *(next) {


  5. console.log('A');

  6. yield next;

  7. console.log('E');


  8. });


  9. // 2

  10. app.use(function *(next) {

  11. console.log('B');

  12. yield next;

  13. console.log('D');

  14. });


  15. // 3

  16. app.use(function *(next) {

  17. console.log('C');

  18. });


  19. app.listen(3000);


当一个新的请求进来的时候,它会流经一系列的中间件(即按照use方法调用的顺序1-2-3)。因此在上面的示例代码中, 请求首先经过第一个中间件,它会首先输出A,然后它遇到了yield next语句,这会使它转入下一个中间件计算相应的结果, 只有在处理完后才回到离开的地方。因此,接下来会转入到下一个中间件打印B,再次碰到yield next, 转入下一个中间件,打印C。现在已经没有更多的中间件了,因此执行完第三个中间件就会回流,首先回到第二个中间件, 继续执行打印出D,中间件2的代码执行完毕,再回流到第一个中间件,打印E。


到目前为止,Koa模块本身并没有什么复杂的地方,因此没有必要再追溯那些你可以在文档中就能知道的信息。 我推荐你直接去阅读Koa的文档去了解关于Koa的详细使用说明,其实也没有什么复杂的内容,所以这里也就不再多介绍了。 这里列出相关文档的链接:



最后让我们再来看一个例子(这个例子来源于Koa的官网),它利用了一些HTTP特性。第一个中间件计算了响应的时间。 你会发现获取响应开始和结束的时间非常简单。并且在Koa中你可以优雅的将这些功能模块分离开。


[js]

  1. app.use(function *(next) {

  2. var start = new Date;

  3. yield next;

  4. var ms = new Date - start;

  5. this.set('X-Response-Time', ms + 'ms');

  6. });


  7. app.use(function *(next) {

  8. var start = new Date;

  9. yield next;

  10. var ms = new Date - start;

  11. console.log('%s %s - %s', this.method, this.url, ms);

  12. });


  13. app.use(function *() {

  14. this.body = 'Hello World';

  15. });


  16. app.listen(3000);


总结


现在你已经熟悉了Koa的, 虽然使用旧的框架也能够完成相关的任务,但是现在你可以尝试Koa这个新的框架来解决以前的问题。因为, 在旧的框架中可能有非常多的功能是你从来都用不到的,或者某些并不是按照你的设想工作的。 现在,以Koa为代表的现代Node框架能够为你带来这些改变。你可以使用更轻量级的核心, 然后通过npm引入你需要的模块到你的app中,这种方式下你能够完全的控制哪些模块是你需要用的。


Links




译者简介:景庄,前端工程师,关注Node.js、前端工程化。个人博客: 。


本文为CSDN原创,点击“阅读原文”可查看完整文章并参与讨论。


以上是关于深入浅出Koa常用工具分享 帮你降低编程难度的主要内容,如果未能解决你的问题,请参考以下文章

深入浅出koa洋葱模型

工厂模式

深入浅出 Koa

12、express 和 koa 有啥关系,有啥区别(高薪常问)

深入node.js koa原理,实现koa

深入node.js koa原理,实现koa