初用Nodejs koa,为啥同一个中间件里的代码会执行两次?

初用Nodejs koa,为啥同一个中间件里的代码会执行两次?

代码如下: var app = require(‘koa’)();

var i = 0;

app.use(function *(next) {
	yield next;
    console.info(i++);
});

app.use(function *(next) {
     yield next;
    this.body = 'Hello, total is ' + i;
});

app.listen(3000);

就这样的代码,每次我刷新页面,控制台的i总是+2,为啥不是+1?页面上的i也是+2,很奇怪。。。哪里的问题呢?


5 回复

favicon?

app.use(function *(next) {
    console.log(this.url);   // 加上这个观察下, 到底收到了几个请求
    yield next;
    console.info(i++);
});

这个问题可以通过分析 Koa 中间件的调用机制来理解。Koa 使用了中间件栈(middleware stack)的概念,每个中间件都会按顺序依次执行,并且通常每个中间件都需要调用 yield next 来确保后续中间件能够被执行。这种设计使得请求在进入和离开中间件时可以进行一些处理。

在你的例子中,有两个中间件被注册到应用中。当一个请求到达时,第一个中间件会被执行,然后它调用 yield next 来执行下一个中间件。第二个中间件执行完毕后,控制权返回到第一个中间件,此时 yield next 之后的代码会被执行。

由于你在这个中间件里使用了一个全局变量 i 来计数,这个变量会在每次请求时被重置为初始值 0。然而,由于 Koa 的中间件机制,每次请求时,这个中间件实际上会被执行两次:一次是在进入时,另一次是在返回时。因此,每次请求时,i 会被累加两次,导致你看到的结果是每次请求时 i 都增加了 2。

为了更好地理解这一点,我们可以修改一下代码,使其更加清晰地展示中间件的执行过程:

var Koa = require('koa');
var app = new Koa();

var i = 0;

app.use(async (ctx, next) => {
  console.log(`Before middleware: ${i}`);
  await next();
  console.log(`After middleware: ${i}`);
  i++;
});

app.use(async ctx => {
  ctx.body = 'Hello, total is ' + i;
});

app.listen(3000);

在这个修改后的代码中,我们添加了一些日志输出,以便更清楚地看到中间件的执行过程。每次请求时,你会看到类似如下的输出:

Before middleware: 0
After middleware: 0
Before middleware: 1
After middleware: 1

这说明每次请求时,中间件确实被调用了两次。第一次调用是在请求进入时,第二次调用是在请求返回时。

如果你希望 i 只增加一次,可以将 i 定义为一个闭包内的局部变量,或者使用其他方式确保其只在特定条件下增加。

请求了 favicon

有可能是favicon 但我有时候重启服务 又只+1而不是+2 看起来没规律一样

当你使用 Koa 框架时,中间件是以栈的形式依次调用的。你提供的代码中,每个中间件都会调用 yield next;,这会导致中间件栈中的下一个中间件被执行。由于你的两个中间件都包含了 yield next;,这就导致了每个请求经过这两个中间件时会被执行两次。

具体来说,当你刷新页面时,请求会进入第一个中间件,执行 console.info(i++);,此时 i 变为 1;然后它会调用 yield next;,导致进入第二个中间件,最后回到第一个中间件完成执行。因此,当请求完全处理完毕后,i 增加了两次。

下面是修改后的代码,你可以看到 i 只会增加一次:

var Koa = require('koa');
var app = new Koa();

let i = 0;

// 第一个中间件
app.use(async (ctx, next) => {
    i++;
    console.info(i);
    await next();
});

// 第二个中间件
app.use(async (ctx, next) => {
    ctx.body = 'Hello, total is ' + i;
});

app.listen(3000);

这样,每次刷新页面时,i 只会增加一次,并且页面显示的 total 值也会正确地反映当前的 i 值。

回到顶部