用过express的同学都知道,一个HTTP请求的完成就是经过若干中间件完成的,中间件是一个可访问请求对象(req
)和响应对象(res
)的函数,在 Express 应用的请求-响应循环里,下一个内联的中间件通常用变量 next
表示。
那么现在我先上一道我面试中遇到的一道题,考的就是中间件的一个实现思路。当然如果你知道express,会更好地帮助你理解。
function fun1(ctx, next) {
ctx.count++;
console.log(ctx.count);
next();
}
function fun2(ctx, next) {
ctx.count++;
console.log(ctx.count);
next();
}
function fun3(ctx, next) {
ctx.count++;
console.log(ctx.count);
next();
}
compose([fun1, fun2, fun3])({count: 1});
这道题的是让你实现一个compose,使得compose([fun1, fun2, fun3])({count: 1})调用后,使得fun1 fun2 fun3依次执行,比如在fun1里面当
next被调用时,就会把控制权交给下一个中间件函数,fun1的下一个中间件函数fun2就会被执行,{count:1}是传入的context,中间件函数就是
依次对context对象进行处理。
我的compose实现思路很简单,如下
function compose(arr){
index = 0;
len = arr.length;
return function (ctx) {
function next() {
index++;
if(index >= len) return;
arr[index](ctx, next);
}
arr[index](ctx, next);
}
}
这样输出的结果是 2 3 4,和预期相符
下面我们来验证一个这个解法对不对。
测试一:如果在某个中间函数体中,用户没有调用next,则不会继续调用下个中间件函数。
function fun1(ctx, next) {
ctx.count++;
console.log(ctx.count);
next();
}
function fun2(ctx, next) {
ctx.count++;
console.log(ctx.count);
// next();
}
function fun3(ctx, next) {
ctx.count++;
console.log(ctx.count);
next();
}
compose([fun1, fun2, fun3])({count: 1});
如上在fun2中没用调用next ,则输出为2 3,和预期相符
测试二:如果next的调用在异步环境里面,则下一个中间件函数必须等上一个中间件函数的next真正被调用(即使是异步)时才会执行。
function fun1(ctx, next) {
ctx.count++;
console.log(ctx.count);
next();
}
function fun2(ctx, next) {
ctx.count++;
console.log(ctx.count);
setTimeout(function () {
next();
},1000);
}
function fun3(ctx, next) {
ctx.count++;
console.log(ctx.count);
next();
}
compose([fun1, fun2, fun3])({count: 1});
如上,在fun2中1s后才调用next,输出为 2 3 然后一秒后输出4 ,符合预期。
当然这是一个最简单的中间件实现思路,在express中间件中远比这复杂,但是这可以考察你解决问题的思路。