Deno中Oak WebSocket中间件await后无法发送echo2消息的问题排查
问题描述
使用下述Deno Oak框架的WebSocket服务器代码时,客户端始终无法接收到echo2消息,但await语句之前的echo1消息可正常接收。移除await语句后,两条消息均能被客户端接收。测试使用Chrome浏览器的WebSocket客户端扩展。
服务器代码:
import { Application, Router } from "https://deno.land/x/oak@v12.1.0/mod.ts"; const app = new Application(); const router = new Router(); async function pause(ms) { return new Promise((resolve) => setTimeout(resolve, ms)); } router.get("/ws", (ctx) => { console.log("ws connected"); if (!ctx.isUpgradable) { ctx.throw(501); } const ws = ctx.upgrade(); ws.onopen = () => { ws.send(JSON.stringify({ intent: "init", id: "test" })); }; ws.addEventListener("message", async (ev) => { const d = JSON.parse(ev.data); console.log("got message", d); switch (d.intent) { case "echo": { ws.send(JSON.stringify({ intent: "echo1", data: { empty: true } })); await pause(1000); ws.send(JSON.stringify({ intent: "echo2", data: { empty: true } })); break; } } }); });
问题原因与解决方案
原因
问题核心在于将message事件的监听器直接定义为async函数:
- Oak基于Deno的WebSocket实现,其事件处理机制中,若监听器是
async函数,当执行到await时,当前事件处理的同步上下文会被释放,事件循环转向处理其他任务,导致WebSocket的内部发送流程被中断,await后的send调用无法正确推送消息到客户端。 - 此外,
async函数返回的Promise会被事件系统忽略,若后续流程出现未捕获错误,也会导致消息发送失败。
解决方案
有两种可行的修复方式:
方式一:用自执行async函数包裹异步逻辑,保持监听器同步
将原本的async监听器改为普通同步函数,内部通过立即执行的async函数处理异步逻辑,同时捕获潜在错误:
ws.addEventListener("message", (ev) => { (async () => { const d = JSON.parse(ev.data); console.log("got message", d); switch (d.intent) { case "echo": { ws.send(JSON.stringify({ intent: "echo1", data: { empty: true } })); await pause(1000); ws.send(JSON.stringify({ intent: "echo2", data: { empty: true } })); break; } } })().catch(err => console.error("处理消息出错:", err)); });
方式二:用setTimeout替代await实现延迟,避免异步上下文干扰
如果不需要复杂的异步流程,直接用setTimeout实现延迟发送,不影响当前事件处理流程:
ws.addEventListener("message", (ev) => { const d = JSON.parse(ev.data); console.log("got message", d); switch (d.intent) { case "echo": { ws.send(JSON.stringify({ intent: "echo1", data: { empty: true } })); setTimeout(() => { ws.send(JSON.stringify({ intent: "echo2", data: { empty: true } })); }, 1000); break; } } });
内容的提问来源于stack exchange,提问作者TimDog
相关产品推荐
相关产品推荐

