Last active
July 9, 2021 07:31
-
-
Save yeyuguo/b0f36f2d6a5db9c89ef4e6ecac56fde5 to your computer and use it in GitHub Desktop.
所有插件注册与执行 (洋葱机制源码实现, 以及的所有场景模拟)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 解决 node 不能接收 try catch 的 promise | |
if(process) { | |
process.on('unhandledRejection', (reason, p) => { | |
console.warn('Unhandled Rejection at: Promise', p, 'reason:', reason); | |
// application specific logging, throwing an error, or other logic here | |
}); | |
} | |
/** | |
* 插件机制 | |
* 优先使用传递参数 | |
* @param {Object} context 对象输入 | |
* @param {Function} lastNext 洋葱机制的最后一次之后的 默认处理函数 | |
* */ | |
export default class PluginRegister{ | |
constructor(context, lastNext) { | |
this.middleware = [] | |
this.context = context | |
this.next = lastNext | |
} | |
/** 建造者模式 | |
* !不能传入参数, 使用 use 来做解耦扩展 | |
*/ | |
run() { | |
PluginRegister.engine(this.middleware)(this.context, this.next) | |
} | |
/** | |
* 注册插件 建造者模式 | |
* todo 单插件的注册, 使用依赖注入 思想提供 option, 参考 shout.js 机制 | |
* @param {Array|Function} plugin | |
*/ | |
use(plugin) { | |
this.middleware = this.middleware.concat(plugin) | |
} | |
/** | |
* 洋葱机制源码 | |
* !设置为 static 静态方法方便外部直接使用洋葱算法 | |
* todo 未来丰富 熔断、跳过、全部链路必须通过实现机制 | |
* todo compose | |
* @param {Array} plugins 插件机制 | |
* @return {Function} | |
*/ | |
static engine(plugins) { | |
return composeOnion(plugins) | |
} | |
} | |
/** | |
* 洋葱机制实现 | |
*/ | |
function composeOnion(plugins) { | |
if(!Array.isArray(plugins)){ | |
throw new TypeError('中间件需要是数组') | |
} | |
for(const _plugin of plugins){ | |
if(typeof _plugin !== 'function') { | |
throw new TypeError('插件必须是函数') | |
} | |
} | |
let _index = -1 // 只要小于0就行 | |
/** | |
* @param {Object} context 传递的上下文对象 | |
* @param {Function} next 迭代向里执行的最后一次 | |
*/ | |
return function(context, next) { | |
dispatch(0) | |
function dispatch(current) { | |
// !当前索引必须大于上一次索引 | |
if(current <= _index) { | |
return Promise.reject(new TypeError('next() 被调用多次')) | |
} | |
_index = current | |
const plgFn = plugins[current] | |
let fn | |
if(_index === plugins.length) { | |
// 多了一次 i+1 空执行, 跳出递归 | |
fn = next | |
} else { | |
fn = plgFn | |
} | |
if(!fn) { | |
return Promise.resolve() | |
} | |
try { | |
return Promise.resolve( | |
fn(context, function next() { | |
return dispatch(current + 1) | |
}) | |
) | |
} catch (error) { | |
console.log('error: ', error); | |
return Promise.reject(error) | |
} | |
} | |
} | |
} | |
(function Test() { | |
// 测试 | |
function test1(context, next) { | |
console.log('test1 前') | |
next() | |
console.log('test1 后') | |
} | |
// 测试 拦截后面所有中间件 | |
function error(context, next) { | |
console.log('error 前') | |
} | |
// 测试 数据变更前后 | |
function error2(context, next) { | |
console.log('context: ', context); | |
context.add = 'yes' | |
next() | |
} | |
// 测试异步 | |
async function error3(context, next) { | |
await new Promise(resolve=>{ | |
setTimeout(()=>{ | |
console.log('error 前', '延迟加载') | |
resolve('tttt') | |
}, 3000) | |
}) | |
next() | |
} | |
// 测试多次 next | |
function error4(context, next) { | |
console.log('error 前') | |
next() | |
// next() | |
} | |
function test3(context, next) { | |
console.log('test3 context: ', context); | |
console.log('test3 前') | |
context.add = 'yes' | |
next() | |
context.after = 'yes' | |
// return Promise.reject('console reject error') | |
console.log('test3 后') // return promise 存在就不打印 | |
} | |
function test4(context, next) { | |
console.log('test4 前') | |
next() | |
console.log('test4 后') | |
} | |
// 完整执行 | |
// use(test1) | |
// // use(error) // 被拦截掉 | |
// use(test3) | |
// use(test4) | |
// 中间过程被拦截 | |
const next = function(context, next){ | |
console.log('context, next: ', context, next); | |
console.log('test 输入的 next') | |
} | |
// composeOnion([test1, error4, test3, test4])({a:1}, next) | |
const instance = new PluginRegister({a: 1}, next) | |
instance.use([test1, error4, test3, test4]) | |
instance.run() | |
})() | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment