原文链接
介绍
在处理一些复杂逻辑的场景下,特别是一些长流程的任务,我们通常喜欢把流程拆分成单步小任务来执行,然后保证这些小任务按照既定的逻辑顺序执行。
或者我们希望对一个实体赋能,让它拥有错误检测、数据过滤、日志打印等功能。把每一项功能都抽成独立的中间件,分别负责独立的任务,就是一个比较好的选择。
koa-compose 模块可以将多个中间件函数合并成一个组合中间件函数,然后通过调用这个中间件函数就可以依次来执行这一系列中间件。
这个模块的源码比较简单,我们直接通过源码来分析一下它的处理过程:
'use strict'
/**
* Expose compositor.
*/
module.exports = compose
/**
* Compose `middleware` returning
* a fully valid middleware comprised
* of all those which are passed.
*
* @param {Array} middleware
* @return {Function}
* @api public
*/
function compose (middleware) {
// 只接收数组
if (!Array.isArray(middleware)) throw new TypeError('Middleware stack must be an array!')
for (const fn of middleware) {
// 只接收 function
if (typeof fn !== 'function') throw new TypeError('Middleware must be composed of functions!')
}
/**
* @param {Object} context
* @return {Promise}
* @api public
*/
// 返回一个匿名执行函数
return function (context, next) {
// last called middleware #
let index = -1
// 递归调用开始入口
return dispatch(0)
function dispatch (i) {
// 避免重复调用
if (i <= index) return Promise.reject(new Error('next() called multiple times'))
// 调用指针
index = i
let fn = middleware[i]
// 当遍历完所有的中间件,执行 next 回调函数
if (i === middleware.length) fn = next
// 如果没有定义 next,直接 返回 undefined
if (!fn) return Promise.resolve()
try {
// 每一个中间件都会有两个形参
// 1.外部透传进来的 context 对象,2.next 回调方法
// 下一个中间件的执行体,会作为上一个中间的 next 参数传递进去
// 通过 next() 方法的调用,实现递归所有中间件
return Promise.resolve(fn(context, dispatch.bind(null, i + 1)));
} catch (err) {
return Promise.reject(err)
}
}
}
}
通过代码我们可以看出 compose 内部的中间件依次执行是通过 next() 来实现的。可以理解成接着往下执行,也可以表示执行下一个中间件。
next
const compose = require('koa-compose')
var compMiddleware = compose([
middleware1,
middleware2
])
var compMiddlewareNext = function () {
console.log('中间件全部执行完毕')
}
compMiddleware('我是 context', compMiddlewareNext)
function middleware1(ctx, next) {
console.log(ctx)
console.log('第一个中间件')
next()
console.log('第一个中间件的next执行后')
}
function middleware2(ctx, next) {
console.log(ctx)
console.log('第二个中间件')
next()
console.log('第二个中间件的next执行后')
}
我是 context
第一个中间件
我是 context
第二个中间件
中间件全部执行完毕
第二个中间件的next执行后
第一个中间件的next执行后
上述代码的执行过程如下:
- 执行 middleware1,输出“第一个中间件”,调用
next()
- 执行 middleware2,输出“第二个中间件”,调用
next()
- 执行 compMiddlewareNext,输出“中间件全部执行完毕”
- 全部中间件执行完毕,执行步骤回到 middleware2 的执行体,输出“第二个中间件的next执行后”
- middleware2 执行完毕后,middleware1 继续执行,输出“第一个中间件的next执行后”
通过下面一段伪代码可能会让执行过程更加清晰一些:
compMiddleware {
middleware1 {
console.log('第一个中间件')
// next() 调用的时候执行 middleware2
middleware2 {
console.log('第二个中间件')
// next()调用的时候执行 compMiddlewareNext()
console.log('中间件全部执行完毕')
console.log('第二个中间件的next执行后')
}
console.log('第一个中间件的next执行后')
}
}
介绍
在处理一些复杂逻辑的场景下,特别是一些长流程的任务,我们通常喜欢把流程拆分成单步小任务来执行,然后保证这些小任务按照既定的逻辑顺序执行。
或者我们希望对一个实体赋能,让它拥有错误检测、数据过滤、日志打印等功能。把每一项功能都抽成独立的中间件,分别负责独立的任务,就是一个比较好的选择。
koa-compose 模块可以将多个中间件函数合并成一个组合中间件函数,然后通过调用这个中间件函数就可以依次来执行这一系列中间件。
这个模块的源码比较简单,我们直接通过源码来分析一下它的处理过程:
通过代码我们可以看出 compose 内部的中间件依次执行是通过
next()来实现的。可以理解成接着往下执行,也可以表示执行下一个中间件。next
上述代码的执行过程如下:
next()next()通过下面一段伪代码可能会让执行过程更加清晰一些: