Koa の next は await か return が必須
◆ await も return もないと次以降のミドルウェアの非同期処理を待たない
◆ 非同期処理以降で設定するレスポンスが未設定なので Not Found になる
◆ 非同期処理以降で設定するレスポンスが未設定なので Not Found になる
これは単純に静的ファイルをサーブする Koa アプリケーションの例です
見つからないパスでは ok という文字を返します
これにテスト用にいろいろ追加して 一旦なにもしないミドルウェアとして追加したら動かなくなりました
これにしばらく悩まさせられました
追加したミドルウェアは next を呼び出すだけ何もしないものです
もっといろいろごちゃごちゃしてたのに削りに削ってここまで来ても動かないです
ミドルウェア自体を消すと動きます
koa-static のファイルサーブ機能をはずしても何故か動きます
他で使ってるのと同じスタンダードなミドルウェアと同じように async 関数にして await にしてみたら動きました
async 関数で Promise を返していても await をしないとダメでした
await で待機しても次にする処理ないのに
なぜかよく分からなかったのですが ちゃんとソースを見て非同期処理の流れを見るとわかりました
次以降が全て同期的なら良いですが非同期処理が入るとその完了を待機しません
そのせいで Koa がミドルウェアの処理が完了したとみなしたタイミングでは まだ ok がレスポンスボディに設定されていません
その状態でレスポンスを送信するので Not Found が表示されるというわけです
今回の koa-static ではファイルを読み取る非同期処理が入るので ok が設定される前に Not Found でレスポンスが返っていました
koa-static じゃなくても これでも Not Found は再現できます
サンプルやミドルウェアライブラリのコードを見ていると async 関数じゃないものもあったので await は待機しない限り必須じゃないものだと思い込んでいました
それらのコードではよく見ると next の返り値の Promise を return しています
これでも 次以降のミドルウェアの完了を待機できます
単純にそこで処理を終えたいからの return かと思えばちゃんと意味があったんですね
というわけで Koa の next を呼び出す場合は return 返り値をするか await での待機が必須です
一応非同期が入らない場合は ただ next を呼び出すだけでも動きますが 将来的に壊れる可能性が高いのでちゃんと return か await しておくべきです
見つからないパスでは ok という文字を返します
const koa = require("koa")
const app = new koa()
app.use(require("koa-static")("."))
app.use(ctx => {
ctx.body = "ok"
})
app.listen(3001)
"/" にアクセスすると ok が表示される
これにテスト用にいろいろ追加して 一旦なにもしないミドルウェアとして追加したら動かなくなりました
const koa = require("koa")
const app = new koa()
app.use((ctx, next) => {
next()
})
app.use(require("koa-static")("."))
app.use(ctx => {
ctx.body = "ok"
})
app.listen(3001)
"/" にアクセスすると Not Found が表示される
これにしばらく悩まさせられました
追加したミドルウェアは next を呼び出すだけ何もしないものです
もっといろいろごちゃごちゃしてたのに削りに削ってここまで来ても動かないです
ミドルウェア自体を消すと動きます
koa-static のファイルサーブ機能をはずしても何故か動きます
const koa = require("koa")
const app = new koa()
app.use((ctx, next) => {
next()
})
app.use(ctx => {
ctx.body = "ok"
})
app.listen(3001)
"/" にアクセスすると ok が表示される
他で使ってるのと同じスタンダードなミドルウェアと同じように async 関数にして await にしてみたら動きました
const koa = require("koa")
const app = new koa()
app.use(async (ctx, next) => {
await next()
})
app.use(require("koa-static")("."))
app.use(ctx => {
ctx.body = "ok"
})
app.listen(3001)
"/" にアクセスすると ok が表示される
async 関数で Promise を返していても await をしないとダメでした
await で待機しても次にする処理ないのに
なぜかよく分からなかったのですが ちゃんとソースを見て非同期処理の流れを見るとわかりました
解決編
await せず next だけ呼び出すと 次以降のミドルウェアの完了を待たずに Promise が resolve されます次以降が全て同期的なら良いですが非同期処理が入るとその完了を待機しません
そのせいで Koa がミドルウェアの処理が完了したとみなしたタイミングでは まだ ok がレスポンスボディに設定されていません
その状態でレスポンスを送信するので Not Found が表示されるというわけです
今回の koa-static ではファイルを読み取る非同期処理が入るので ok が設定される前に Not Found でレスポンスが返っていました
koa-static じゃなくても これでも Not Found は再現できます
const koa = require("koa")
const app = new koa()
app.use((ctx, next) => {
next()
})
app.use(async ctx => {
await new Promise(r => setTimeout(r, 1000))
ctx.body = "ok"
})
app.listen(3001)
"/" にアクセスすると Not Found が表示される
サンプルやミドルウェアライブラリのコードを見ていると async 関数じゃないものもあったので await は待機しない限り必須じゃないものだと思い込んでいました
それらのコードではよく見ると next の返り値の Promise を return しています
これでも 次以降のミドルウェアの完了を待機できます
const koa = require("koa")
const app = new koa()
app.use((ctx, next) => {
return next()
})
app.use(require("koa-static")("."))
app.use(ctx => {
ctx.body = "ok"
})
app.listen(3001)
"/" にアクセスすると ok が表示される
単純にそこで処理を終えたいからの return かと思えばちゃんと意味があったんですね
というわけで Koa の next を呼び出す場合は return 返り値をするか await での待機が必須です
一応非同期が入らない場合は ただ next を呼び出すだけでも動きますが 将来的に壊れる可能性が高いのでちゃんと return か await しておくべきです