洋葱模型是一种中间件流程控制方式。顾命就是用来控制流程的,我这里就讲解的很简单,主要把实现的思路做一个简单的讲解。
什么是洋葱模型
先看一张图:
这张图在网上特别流行,基本搜索一下洋葱模型每篇文章都有这张图来做讲解。比较形象的解释了洋葱模型是在处理请求来和响应请求之间的问题。可以类比栈,先进后出。
express的洋葱模型
大家只要讲洋葱模型,就会联想到koa
的中间件,很少有人谈及express
的洋葱模型和中间件原理。那么我就来反其道而行之,讲讲express
的洋葱模型。以下是栗子:
const express = require('express')
const app = express();
const A = function A(req,res,next) {
console.log('A 开始')
next()
console.log('A 结束')
}
const B = function B(req,res,next) {
console.log('B 开始')
next()
console.log('B 结束')
}
const C = function C(req,res,next) {
console.log('C 开始')
next()
console.log('C 结束')
}
app.get('/',A,B,C)
A 开始
B 开始
C 开始
C 结束
B 结束
A 结束
以上代码可以直接复制,直接运行,运行结果就是
A=>B=>C=B=>A
的结构,正如洋葱模型的结构。以上代码其实等价于以下代码,你可以理解为3个函数的调用是嵌套的,A
点用B
,B
调用C
,C
结束释放,B
结束释放,A
结束释放的这么的一个流程。
function A() {
console.log('A 开始')
function B() {
console.log('B 开始')
function C() {
console.log('C 开始')
console.log('C 结束')
}
C()
console.log('B 结束')
}
B()
console.log('A 结束')
}
如何实现
大概的一个思想和逻辑我们已经了解,那该怎么将一个顺序执行的变成内部调用的逻辑呢?
let index = -1
const FnArr = [A,B,C]
function next() {
index++
FnArr[index](next)
if(index >= FnArr.length) {
return
}
}
next()
实现的逻辑非常简单,每次迭代都是将next传递给用户,用户手动调用next之后就会跳到下个函数的执行,以此来达到内部迭代的目的。
express内部实现
next();
function next(err) {
// signal to exit route
if (err && err === 'route') {
return done();
}
// signal to exit router
if (err && err === 'router') {
return done(err)
}
var layer = stack[idx++];
if (!layer) {
return done(err);
}
if (layer.method && layer.method !== method) {
return next(err);
}
if (err) {
layer.handle_error(err, req, res, next);
} else {
layer.handle_request(req, res, next);
}
}
espress
中next
就是用来迭代中间件的,handle_request
可以认为就用传进来的方法也就是A,B,C
每次迭代都会把当前的next
传给用户,用户手动调用的时候,就会再次触动这个next
函数,利用了闭包保留了idx
和stack
。
到这里就结束了,希望对你有些帮助。