compose函数实现

  • 每一个中间件需要封装一个 Promise
  • 洋葱模型的先进后出操作,对应Promise.resolve的前后操作
  1. const middleware1 = async (ctx, next) => {
  2. console.log(1);
  3. await next();
  4. console.log(6);
  5. };
  6. const middleware2 = async (ctx, next) => {
  7. console.log(2);
  8. await next();
  9. console.log(5);
  10. };
  11. const middleware3 = async (ctx, next) => {
  12. console.log(3);
  13. await next();
  14. console.log(4);
  15. };
  16. const fn = compose([middleware1, middleware2, middleware3]);
  17. fn().then(() => {
  18. console.log("end");
  19. });
  20. function compose(middlewares) {
  21. if (!Array.isArray(middlewares)) {
  22. throw new TypeError("middle need a array");
  23. }
  24. return function (ctx, next) {
  25. let index = -1;
  26. // 第一次调用
  27. return dispatch(0);
  28. function dispatch(i) {
  29. if (i < index) {
  30. return Promise.reject(new Error("next() call multiple times"));
  31. }
  32. index = i;
  33. let fn = middlewares[i];
  34. if (i === middlewares.length) {
  35. fn = next;
  36. }
  37. if (!fn) {
  38. return Promise.resolve();
  39. }
  40. try {
  41. return Promise.resolve(
  42. fn(ctx, () => {
  43. return dispatch(i + 1);
  44. })
  45. );
  46. } catch (e) {
  47. return Promise.reject(e);
  48. }
  49. }
  50. };
  51. }
  52. // 结果显示
  53. // "1"
  54. // "2"
  55. // "3"
  56. // "4"
  57. // "5"
  58. // "6"
  59. // "end"

结合http与events模块实现

  1. const http = require("http");
  2. const Emitter = require("events");
  3. const context = {
  4. _body: null,
  5. get body() {
  6. return this._body;
  7. },
  8. set body(val) {
  9. this._body = val;
  10. this.res.end(this._body);
  11. },
  12. };
  13. function compose(middlewares) {
  14. if (!Array.isArray(middlewares)) {
  15. throw new TypeError("middle need a array");
  16. }
  17. return function (ctx, next) {
  18. let index = -1;
  19. // 第一次调用
  20. return dispatch(0);
  21. function dispatch(i) {
  22. if (i < index) {
  23. return Promise.reject(new Error("next() call multiple times"));
  24. }
  25. index = i;
  26. let fn = middlewares[i];
  27. if (i === middlewares.length) {
  28. fn = next;
  29. }
  30. if (!fn) {
  31. return Promise.resolve();
  32. }
  33. try {
  34. return Promise.resolve(
  35. fn(ctx, () => {
  36. return dispatch(i + 1);
  37. })
  38. );
  39. } catch (e) {
  40. return Promise.reject(e);
  41. }
  42. }
  43. };
  44. }
  45. class MyKoa extends Emitter {
  46. constructor() {
  47. super();
  48. this.middleware = [];
  49. this.context = Object.create(context);
  50. }
  51. /**
  52. * 服务端监听
  53. * @param {...any} args
  54. */
  55. listen(...args) {
  56. const server = http.createServer(this.callback());
  57. return server.listen(...args);
  58. }
  59. /**
  60. * 注册使用中间件
  61. * @param {*} fn
  62. */
  63. use(fn) {
  64. if (typeof fn === "function") {
  65. this.middleware.push(fn);
  66. }
  67. }
  68. /**
  69. * 中间件回调方法
  70. */
  71. callback() {
  72. let that = this;
  73. if (this.listeners("error").length === 0) {
  74. this.on("error", this.onerror);
  75. }
  76. const handleRequest = (req, res) => {
  77. let context = this.createContext(req, res);
  78. let middleware = this.middleware;
  79. compose(middleware)(context).catch((err) => this.onerror(err));
  80. };
  81. return handleRequest;
  82. }
  83. /**
  84. * 异常处理监听
  85. * @param {EndOfStreamError} err
  86. */
  87. onerror(err) {
  88. console.log(err);
  89. }
  90. /**
  91. * 创建通用上下文
  92. * @param {Object} req
  93. * @param {Object} res
  94. */
  95. createContext(req, res) {
  96. let context = Object.create(this.context);
  97. context.req = req;
  98. context.res = res;
  99. return context;
  100. }
  101. }
  102. const app = new MyKoa();
  103. const PORT = 3001;
  104. app.use(async (ctx) => {
  105. ctx.body = "<p>this is a body</p>";
  106. });
  107. app.listen(PORT, () => {
  108. console.log(`the web server is starting at port ${PORT}`);
  109. });