This paper simply introduces the principle of onion model
The classic example
const Koa = require('koa');
const app = new Koa();
app.use(async (ctx, next) => {
console.log('1');
await next();
console.log(1-1 ' ');
});
app.use(async (ctx, next) => {
console.log('2');
await next();
console.log('2-2');
});
app.use(async (ctx, next) => {
console.log('3');
await next();
console.log('3-2');
});
app.listen(3000);
Copy the code
The output
Access 127.0.0.1:3000 and the result is as follows
1
2
3
32 -
22 -
1- 1
Copy the code
This is the classic KOA onion model
So why?
Refer to the source code to construct oneApp
The class of
class App {
constructor() {
// Define the middleware array
this.middleware = [];
}
use(fn) {
if (fn && typeoffn ! = ="function") throw new Error(The input must be a function.);
// The fn input is passed to the Middleware array
this.middleware.push(fn); } listen(... arg) {Const server = http.createserver (this.callback()); const server = http.createserver (this.callback()); * return server.listen(... args); * /
this.callback();
}
callback() {
const fn = compose(this.middleware);
return this.handleRequest(fn);
}
handleRequest(fnMiddleware) {
return fnMiddleware()
.then((a)= > { console.log('over'); })
.catch((err) = > { console.log(err); }); }}Copy the code
App
Defines thethis.middleware
So an array like this is used to putapp.use(fn)
The incoming middleware methodfn
app.listen(3000)
It’s actually creating oneHTTP
Server (this step is omitted for simplicity in this article).this.callback()
ishttp.createServer()
To handlehttp
Request (ps. Can also passapp.callback()
Method to rewrite)this.callback()
Well, it’s actually executingcompose(this.middleware)
Function returns the result
So let’s seecompose
This function
// Simplify the compose method in the koa-compose source code
function compose(middleware) {
return function (context, next) {
return dispatch(0)
function dispatch(i) {
let fn = middleware[i]
if (i === middleware.length) fn = next
if(! fn)return Promise.resolve()
try {
return Promise.resolve(fn(context, dispatch.bind(null, i + 1)));
} catch (err) {
return Promise.reject(err)
}
}
}
}
Copy the code
Compose (this.middleware) returns a function fn that traverses the implementing middleware method this.middleware
Using the example at the beginning of this article, let’s see how FN performs
// dispatch(0)
Promise.resolve((async (ctx, next) = > {
console.log('1');
await next();
console.log(1-1 ' ');
})(context, dispatch.bind(null.1)));
// Next in dispatch(0) is dispatch.bind(null, 1)
// So next() is equivalent to dispatch(1)
Promise.resolve((async (ctx, next) = > {
console.log('2');
await next();
console.log('2-1');
})(context, dispatch.bind(null.2)));
// Next in dispatch(1) is dispatch.bind(null, 2)
// So next() is equivalent to dispatch(2)
Promise.resolve((async (ctx, next) = > {
console.log('3');
await next();
console.log('3-1');
})(context, dispatch.bind(null.3)));
// This.middleware[3] does not exist, so a promise.resolve () is returned;
Copy the code
So according to the order of execution of the code, it’s 1, 2, 3, 3-1, 2-1, 1-1
Error trapping
As you can see, fnMiddleware() is followed by then and catch functions, indicating that any errors that occur during middleware execution will be caught by the last catch