See the Pen Koa.js compose by Willard (@ifyour) on CodePen.

    1. // Koa.js 核心代码 compose
    2. // 决定了 Koa.js 中间件的调用顺序,也就是洋葱模型
    3. function compose(middleware) {
    4. return function(context, next) {
    5. // 最后调用的 middleware 索引
    6. let index = -1;
    7. return dispatch(0);
    8. function dispatch(i) {
    9. if (i <= index)
    10. return Promise.reject(new Error("next() called multiple times"));
    11. index = i;
    12. let fn = middleware[i];
    13. if (i === middleware.length) fn = next;
    14. if (!fn) return Promise.resolve(); // 结束递归条件
    15. try {
    16. return Promise.resolve(
    17. fn(context, function next() {
    18. return dispatch(i + 1); // 递归调用自身
    19. })
    20. );
    21. } catch (err) {
    22. return Promise.reject(err);
    23. }
    24. }
    25. };
    26. }
    27. async function a (ctx, next) {
    28. console.log(1);
    29. const hello = await Promise.resolve('Hello, node.js');
    30. console.log(hello);
    31. await next();
    32. console.log('a end');
    33. }
    34. async function b (ctx, next) {
    35. console.log(2);
    36. const hello = await Promise.resolve('Hello, node.js');
    37. console.log(hello);
    38. await next();
    39. console.log('b end');
    40. }
    41. //
    42. // Demo:测试中间件调用
    43. //
    44. const myCtx = {};
    45. compose([a, b])(myCtx);

    执行结果:

    1. 1
    2. "Hello, node.js"
    3. 2
    4. "Hello, node.js"
    5. "b end"
    6. "a end"