详解express与koa中间件模式对比

2020-06-17 06:45:49易采站长站整理

ctx.set('X-Response-Time', `${ms}ms`);
});

// logger

app.use(async (ctx, next) => {
const start = Date.now();
await next();
const ms = Date.now() - start;
console.log(`${ctx.method} ${ctx.url} - ${ms}`);
});

// response

app.use(async ctx => {
ctx.body = 'Hello World';
});

app.listen(3000);

很明显,当koa处理中间件遇到await next()的时候会暂停当前中间件进而处理下一个中间件,最后再回过头来继续处理剩下的任务,虽然说起来很复杂,但是直觉上我们会有一种隐隐熟悉的感觉:不就是回调函数吗。这里暂且不说具体实现方法,但是确实就是回调函数。跟async/await的特性并无任何关系。

源码简析

connect与koa中间件模式区别的核心就在于next的实现,让我们简单看下二者next的实现。

connect

connect的源码相当少加上注释也就200来行,看起来也很清楚,connect中间件处理在于proto.handle这个私有方法,同样next也是在这里实现的


// 中间件索引
var index = 0
function next(err) {

// 递增
var layer = stack[index++];

// 交由其他部分处理
if (!layer) {
defer(done, err);
return;
}

// route data
var path = parseUrl(req).pathname || '/';
var route = layer.route;

// 递归
// skip this layer if the route doesn't match
if (path.toLowerCase().substr(0, route.length) !== route.toLowerCase()) {
return next(err);
}

// call the layer handle
call(layer.handle, route, err, req, res, next);
}

删掉混淆的代码后 我们可以看到next实现也很简洁。一个递归调用顺序寻找中间件。不断的调用next。代码相当简单但是思路却很值得学习。

其中 done 是第三方处理方法。其他处理sub app以及路由的部分都删除了。不是重点

koa

koa将next的实现抽离成了一个单独的包,代码更加简单,但是实现了一个貌似更加复杂的功能


function compose (middleware) {
return function (context, next) {
// last called middleware #
let index = -1
return dispatch(0)
function dispatch (i) {
index = i
try {
return Promise.resolve(fn(context, function next () {
return dispatch(i + 1)
}))
} catch (err) {
return Promise.reject(err)
}
}
}
}

看着上面处理过的的代码 有些同学可能还是会不明觉厉。

那么我们继续处理一下:


function compose (middleware) {

return function (context, next) {
// last called middleware #
let index = -1
return dispatch(0)