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
相关产品推荐
相关产品推荐

