You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 00:05:33