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

如何安全创建SignalR客户端共享连接?解决多组件竞态条件

解决JavaScript SignalR客户端单例连接的竞态问题

你的问题核心是异步单例的竞态条件:当第一个组件调用GetService()还在等待await service.start()完成时,第二个组件调用时发现this.service仍然是undefined,就会重新创建新的实例和连接。

修复方案:用Promise存储初始化状态

把静态成员从存储实例改成存储初始化Promise,这样所有后续调用都会等待同一个Promise完成,确保只创建一次连接。修改后的代码如下:

export class MessagingService {
    // 存储初始化Promise,而不是直接存实例
    private static instancePromise: Promise<MessagingService> | null = null;

    static GetService(): Promise<MessagingService> {
        if (!this.instancePromise) {
            // 创建Promise,内部完成实例初始化和连接启动
            this.instancePromise = (async () => {
                const service = new MessagingService();
                await service.start();
                return service;
            })();
        }
        return this.instancePromise;
    }

    connection: signalR.HubConnection;

    private constructor() {
        // 把连接构建移到构造函数,避免重复创建
        this.connection = new signalR.HubConnectionBuilder()
            .withUrl("/MyHub")
            .withAutomaticReconnect()
            .build();
    }

    async start() {
        // 如果连接已经启动,直接返回,避免重复调用start
        if (this.connection.state === signalR.HubConnectionState.Connected) {
            return;
        }
        await this.connection.start();
    }
}

关键改动说明

  • 用Promise存储初始化过程:第一个调用时创建初始化Promise,后续调用直接返回这个Promise,不管初始化有没有完成,都不会重复创建实例。
  • 构造函数私有化:防止外部直接new MessagingService(),强制通过GetService()获取实例。
  • 连接构建移到构造函数:确保每个实例只构建一次连接对象。
  • start方法增加状态判断:避免重复调用connection.start()导致错误。

额外注意点

如果连接初始化失败(比如网络问题),instancePromise会处于rejected状态,后续调用都会失败。如果需要支持重试,可以在捕获错误后重置instancePromise为null,下次调用会重新尝试初始化:

static GetService(): Promise<MessagingService> {
    if (!this.instancePromise) {
        this.instancePromise = (async () => {
            try {
                const service = new MessagingService();
                await service.start();
                return service;
            } catch (err) {
                // 初始化失败,重置Promise,允许下次重试
                this.instancePromise = null;
                throw err;
            }
        })();
    }
    return this.instancePromise;
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 01:20:39