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

React(TypeScript)中WebSocket控制流困惑及延迟连接诉求

关于React(TypeScript)中WebSocket的两个问题解答

一、为什么先创建WebSocket再绑定事件不会出现竞态条件?

原生WebSocket的连接建立是异步触发的宏任务,而你创建对象后绑定onopen/onmessage的代码属于同步执行逻辑。浏览器的事件循环规则是:先执行完当前同步任务队列里的所有代码,再去处理异步宏任务(比如WebSocket连接成功的事件回调)。

也就是说,哪怕WebSocket连接建立得再快,onopen事件的触发时机也一定在你绑定完回调函数之后,绝不会出现“连接成功但回调还没绑定”的情况,你的测试代码能正常运行就是这个原因。

二、如何实现延迟连接的WebSocket(配合Jotai集中管理)

原生WebSocket构造函数确实一调用就会发起连接,没法直接实现“先创建对象再手动触发连接”,但可以通过封装类来实现这个逻辑:

封装延迟连接的WebSocket类

class DeferredWebSocket {
  private ws: WebSocket | null = null;
  private url: string;
  private onOpenCallback?: (event: Event) => void;
  private onMessageCallback?: (event: MessageEvent) => void;

  constructor(url: string) {
    this.url = url;
  }

  // 设置onopen回调
  onOpen(callback: (event: Event) => void) {
    this.onOpenCallback = callback;
    // 如果已经连接完成,直接触发回调
    if (this.ws?.readyState === WebSocket.OPEN) {
      callback(new Event('open'));
    }
  }

  // 设置onmessage回调
  onMessage(callback: (event: MessageEvent) => void) {
    this.onMessageCallback = callback;
    if (this.ws) {
      this.ws.onmessage = callback;
    }
  }

  // 手动触发连接
  connect() {
    // 避免重复发起连接
    if (this.ws?.readyState === WebSocket.CONNECTING || this.ws?.readyState === WebSocket.OPEN) {
      return;
    }
    this.ws = new WebSocket(this.url);
    this.ws.onopen = (event) => {
      this.onOpenCallback?.(event);
    };
    this.ws.onmessage = (event) => {
      this.onMessageCallback?.(event);
    };
  }

  // 发送消息
  send(data: string) {
    if (this.ws?.readyState === WebSocket.OPEN) {
      this.ws.send(data);
    }
  }

  // 关闭连接
  close(code?: number, reason?: string) {
    this.ws?.close(code, reason);
    this.ws = null;
  }
}

配合Jotai集中管理

在Jotai中创建atom存储封装后的实例,按需调用connect触发连接:

import { atom, useAtomValue } from 'jotai';
import constants from './constants';

// 创建DeferredWebSocket实例的atom
export const wsAtom = atom(new DeferredWebSocket(constants.WS_URI));

// 组件中使用示例
function ChatComponent() {
  const ws = useAtomValue(wsAtom);
  const user = useAtomValue(userAtom); // 假设存在存储用户信息的atom

  useEffect(() => {
    // 绑定回调
    ws.onOpen(() => {
      ws.send(JSON.stringify({
        command: "login",
        token: "Bearer " + user.token,
      }));
    });

    ws.onMessage((event) => {
      const data = JSON.parse(event.data);
      console.log(`**${data.username}**: ${data.message}\n`);
    });

    // 组件挂载时触发连接(也可绑定到按钮点击等交互事件)
    ws.connect();

    // 组件卸载时关闭连接
    return () => {
      ws.close();
    };
  }, [ws, user]);

  return <div>聊天组件</div>;
}

这样就能实现“先创建实例,之后手动触发连接”的需求,同时通过Jotai集中管理WebSocket实例。

内容的提问来源于stack exchange,提问作者Teekin

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 06:25:54