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

Angular中SignalR服务无法向组件传数据:BehaviorSubject观察者丢失

问题描述

我需要在收到服务器askServerResponse调用时,通过BehaviorSubject将Angular Service的数据传递到Component,但遇到以下问题:

  • 调试发现,在Service的hubConnection.on回调中,onlineUsersSubject的观察者为空,同时存在未抛出的严格模式错误(涉及arguments和caller属性);但Chat Component订阅后,onlineUsersSubject的观察者状态正常。
  • 除服务器监听器内的调用外,其他地方调用onlineUsersSubject.next()均正常工作;且服务器监听器中调用Service内其他Subject的next()也无问题。
  • 控制台无报错,但数据无法传递到Component。

相关代码如下:

signal-r.service.ts

import * as signalR from "@microsoft/signalr";
import {BehaviorSubject} from "rxjs";

@Injectable({
  providedIn: 'root'
})
export class SignalRService {
  public onlineUsersSubject = new BehaviorSubject<string>("abcd");

  startConnection = () => {
    return new Promise((resolve,reject) => {
      this.hubConnection = new signalR.HubConnectionBuilder()
        .withUrl('https://localhost:7024/toastr', {
          skipNegotiation: true,
          transport: signalR.HttpTransportType.WebSockets
        })
        .withAutomaticReconnect([0, 1000, 5000])
        .build();

      this.setServerListener();

      this.hubConnection
        .start()
        .then((data) => {
          this.onlineUsersSubject.next("!!!!"); // 此调用在Chat组件中无响应
          console.log('Hub Connection Started! ' + data);
          resolve('Hub Connection Started! '+ data);
        })
        .catch(err => {
          console.log('An Error occurred: ' + err);
          reject(err);
        })

    });
  }

  setServerListener() {
    this.hubConnection.on("askServerResponse", (someText) => {
      // someText已确认不为空
      // 调试此处时,Chat组件的观察者已丢失
      this.onlineUsersSubject.next(someText);
    })
  }
}

chat.component.ts

import {SignalRService} from "../../signal-r.service";
export class ChatComponent {

  constructor(private signalR: SignalRService) {
    this.signalR.onlineUsersSubject.subscribe({
      next:(res) => {
        console.warn("in component:", res);// 预期打印但无输出
      },
      error:(err)=>{
        console.log(err);
      }
    });
  }
}
解决方案

1. 修复回调函数的this指向问题

SignalR的回调函数可能会改变this的上下文,导致你访问的onlineUsersSubject并非Service实例中的对象。可以通过以下两种方式解决:

方式一:使用箭头函数

箭头函数会继承外部上下文的this,确保指向Service实例:

setServerListener() {
  this.hubConnection.on("askServerResponse", (someText) => {
    console.log("收到服务器数据:", someText);
    this.onlineUsersSubject.next(someText);
  });
}

方式二:手动绑定this

通过bind方法将回调函数的this绑定到Service实例:

setServerListener() {
  const handler = function(someText) {
    this.onlineUsersSubject.next(someText);
  }.bind(this);
  this.hubConnection.on("askServerResponse", handler);
}

2. 规范Subject的暴露方式(可选)

为避免外部直接修改Subject,建议将Subject私有化,对外暴露只读Observable:

// 在SignalRService中修改
private onlineUsersSubject = new BehaviorSubject<string>("abcd");
public onlineUsers$ = this.onlineUsersSubject.asObservable();

然后在Component中订阅这个Observable:

// 在ChatComponent中修改
this.signalR.onlineUsers$.subscribe({
  next:(res) => {
    console.warn("in component:", res);
  },
  error:(err)=>{
    console.log(err);
  }
});

3. 调整订阅时机

确保Component的订阅操作在SignalR连接建立前完成。建议在ngOnInit生命周期钩子中订阅(而非构造函数),更符合Angular的生命周期规范:

import { OnInit } from '@angular/core';
import {SignalRService} from "../../signal-r.service";

export class ChatComponent implements OnInit {
  constructor(private signalR: SignalRService) {}

  ngOnInit(): void {
    this.signalR.onlineUsers$.subscribe({
      next:(res) => {
        console.warn("in component:", res);
      },
      error:(err)=>{
        console.log(err);
      }
    });
    // 若未提前启动连接,可在此处调用
    // this.signalR.startConnection();
  }
}

4. 处理严格模式警告

严格模式下禁止访问arguments.callee和caller,这是SignalR内部代码可能触发的警告,若不影响功能可忽略。若要解决,可升级SignalR到最新稳定版本,官方可能已修复相关兼容性问题。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.21 22:54:19