Socket.io 多次调用事件侦听器 (angular 9)
Socket.io event listener called multiple times (angular 9)
前端 (Angular 9)
我正在使用 Socket.io 和 angular 实现聊天功能 9. 创建了一个服务并调用套接字连接和事件处理,如下所示。
@Injectable({
providedIn: 'root',
})
export class ChatService {
private socket;
public channelError = new BehaviorSubject(null);
public channelHistory = new BehaviorSubject(null);
public channelMessage = new BehaviorSubject(null);
public channelReaction = new BehaviorSubject(null);
constructor(
) {}
establishSocketConnection = (userId) => {
this.socket = io(`${chatUrl}/chat`, {
path: '/socket.io',
query: {
user: userId,
},
});
this.socket.on('channel-history', (threads: any) => {
this.channelError.next('');
this.channelHistory.next(threads);
});
this.socket.on('message-local', (message: any) => {
this.channelMessage.next(message);
});
}
// socket (emit) events
public emitMessage = (message, authorId, channel) => {
this.socket.emit('message', message, authorId, channel);
}
public loadChannelHistory = (channelId) => {
this.socket.emit('load-channel-history', channelId);
}
// socket (on) event listeners
public getChannelHistory(): Observable<any> {
return this.channelHistory.asObservable();
}
public getMessage(): Observable<any> {
return this.channelMessage.asObservable();
}
}
我在登录后调用方法“establishSocketConnection”一次,以建立套接字连接并注册套接字侦听器。我已经用这个方法注册了所有的套接字监听器。
import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { LoginService } from '../../services/login.service';
import { StorageService } from '../../services/storage.service';
import { ProductTypeService } from '../../services/product-type.service';
import { UserSidebarService } from '../../services/user-sidebar.service';
import { Storage } from '@ionic/storage';
import { ChannelTypeService } from 'src/app/services/channel-type.service';
import { ChatService } from 'src/app/services/chat.service';
@Component({
selector: 'app-menu',
templateUrl: './menu.page.html',
styleUrls: ['./menu.page.scss'],
})
export class MenuPage implements OnInit {
activeSettingChild: any;
channelMaintitle: any;
channels: any;
selectedChat: any;
constructor(
private router: Router,
public loginServ: LoginService,
public storageServ: StorageService,
public productTypeServ: ProductTypeService,
public userSidebarServ: UserSidebarService,
public storage: Storage,
private channelTypeServ: ChannelTypeService,
private chatService: ChatService
) {
}
ngOnInit() {
//establish socket connection
let userId = this.storageServ.get('userId');
if (userId) {
this.chatService.establishSocketConnection(userId);
}
this.selectedChat = localStorage.getItem('selectedChat');
this.getAllChannels();
}
getAllChannels() {
this.channelTypeServ.getAllChannelTypes().subscribe(
(channel: any) => {
this.channels = channel.data.channels;
},
(error) => { }
);
}
onSelectChannel(channel) {
this.activeSettingChild = channel.slug;
this.storageServ.set('activeSettingChild', channel.slug);
this.storageServ.set('lockedPageContent', channel.lockedPageContent);
this.storageServ.set('channelTitle', channel.title);
this.storageServ.set('channelId', JSON.stringify(channel));
this.channelMaintitle = channel.settingId.title;
this.storageServ.set('channelMaintitle', this.channelMaintitle);
this.router.navigate(
[`/platinum-chat/${this.selectedChat}/${'channel/' + channel.slug}`],
{ replaceUrl: true }
);
}
}
我在一个组件中按如下方式使用此聊天服务。
组件:
import { Component, OnInit } from '@angular/core';
import { ChatService } from 'src/app/services/chat.service';
@Component({
selector: 'app-platinum-chat',
templateUrl: './platinum-chat.page.html',
styleUrls: ['./platinum-chat.page.scss'],
})
export class PlatinumChatPage implements OnInit {
message: string;
authorId: any;
channel: any;
threadChat: any[] = [];
channelError: string = '';
constructor(
private chatService: ChatService,
) {
this.channel = localStorage.getItem('channelId');
}
ngOnInit() {
//load channel history
this.loadChannelHistory();
// get new message listener
this.chatService.getMessage().subscribe((msg: any) => {
this.threadChat.push(msg);
});
// error listener
this.chatService.getChannelError().subscribe(errorMessage => {
this.channelError = errorMessage;
});
//channel history listener
this.chatService.getChannelHistory().subscribe((threads: any) => {
this.threadChat = threads;
});
}
loadChannelHistory(){
const channelId = JSON.parse(this.channel);
this.chatService.loadChannelHistory(channelId && channelId._id || null);
}
onSendMessage() {
this.chatService.emitMessage(this.message, this.authorId, this.channel);
}
}
问题是,当我发送新消息时,它会多次调用“本地消息”事件侦听器。当我围绕这个问题进行调试时,根据一些建议,它是重复事件侦听器注册的问题。就我而言,我不会多次调用方法“establishSocketConnection”,然后它如何注册重复的侦听器(消息本地)并多次调用新消息。
注意:不是服务器端的问题。我只从服务器端发送单次发射,但它在前端被多次调用。
这里记录了我的问题
https://www.loom.com/share/d9142c73c6c54cb58802ac3edf704bc5
我不明白当前代码库有什么问题。
你们能帮我解决这个问题吗?提前致谢!!
无论其价值如何,都不要让订阅闲置。每次加载组件时都会添加一个订阅,但不会删除。这可能是,但可能不是原因。无论如何,我重构并实现了一种取消订阅()的方法,试一试。
@Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
})
export class PlatinumChatPage implements OnInit, OnDestroy {
message: string;
authorId: any;
channel: any;
threadChat: any[] = [];
channelError: string = '';
subs = [];
constructor(
private chatService: ChatService,
) {
this.channel = localStorage.getItem('channelId');
}
ngOnDestroy() {
this.subs.forEach(sub => sub.unsubscribe());
}
ngOnInit() {
//load channel history
this.loadChannelHistory();
// get new message listener
const chatSub = this.chatService.getMessage().subscribe((msg: any) => {
this.threadChat.push(msg);
});
// error listener
const errorSub = this.chatService.getChannelError().subscribe(errorMessage => {
this.channelError = errorMessage;
});
//channel history listener
const historySub = this.chatService.getChannelHistory().subscribe((threads: any) => {
this.threadChat = threads;
});
this.subs.push(chatSub, errorSub, historySub);
}
loadChannelHistory(){
const channelId = JSON.parse(this.channel);
this.chatService.loadChannelHistory(channelId && channelId._id || null);
}
onSendMessage() {
this.chatService.emitMessage(this.message, this.authorId, this.channel);
}
}
前端 (Angular 9)
我正在使用 Socket.io 和 angular 实现聊天功能 9. 创建了一个服务并调用套接字连接和事件处理,如下所示。
@Injectable({
providedIn: 'root',
})
export class ChatService {
private socket;
public channelError = new BehaviorSubject(null);
public channelHistory = new BehaviorSubject(null);
public channelMessage = new BehaviorSubject(null);
public channelReaction = new BehaviorSubject(null);
constructor(
) {}
establishSocketConnection = (userId) => {
this.socket = io(`${chatUrl}/chat`, {
path: '/socket.io',
query: {
user: userId,
},
});
this.socket.on('channel-history', (threads: any) => {
this.channelError.next('');
this.channelHistory.next(threads);
});
this.socket.on('message-local', (message: any) => {
this.channelMessage.next(message);
});
}
// socket (emit) events
public emitMessage = (message, authorId, channel) => {
this.socket.emit('message', message, authorId, channel);
}
public loadChannelHistory = (channelId) => {
this.socket.emit('load-channel-history', channelId);
}
// socket (on) event listeners
public getChannelHistory(): Observable<any> {
return this.channelHistory.asObservable();
}
public getMessage(): Observable<any> {
return this.channelMessage.asObservable();
}
}
我在登录后调用方法“establishSocketConnection”一次,以建立套接字连接并注册套接字侦听器。我已经用这个方法注册了所有的套接字监听器。
import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { LoginService } from '../../services/login.service';
import { StorageService } from '../../services/storage.service';
import { ProductTypeService } from '../../services/product-type.service';
import { UserSidebarService } from '../../services/user-sidebar.service';
import { Storage } from '@ionic/storage';
import { ChannelTypeService } from 'src/app/services/channel-type.service';
import { ChatService } from 'src/app/services/chat.service';
@Component({
selector: 'app-menu',
templateUrl: './menu.page.html',
styleUrls: ['./menu.page.scss'],
})
export class MenuPage implements OnInit {
activeSettingChild: any;
channelMaintitle: any;
channels: any;
selectedChat: any;
constructor(
private router: Router,
public loginServ: LoginService,
public storageServ: StorageService,
public productTypeServ: ProductTypeService,
public userSidebarServ: UserSidebarService,
public storage: Storage,
private channelTypeServ: ChannelTypeService,
private chatService: ChatService
) {
}
ngOnInit() {
//establish socket connection
let userId = this.storageServ.get('userId');
if (userId) {
this.chatService.establishSocketConnection(userId);
}
this.selectedChat = localStorage.getItem('selectedChat');
this.getAllChannels();
}
getAllChannels() {
this.channelTypeServ.getAllChannelTypes().subscribe(
(channel: any) => {
this.channels = channel.data.channels;
},
(error) => { }
);
}
onSelectChannel(channel) {
this.activeSettingChild = channel.slug;
this.storageServ.set('activeSettingChild', channel.slug);
this.storageServ.set('lockedPageContent', channel.lockedPageContent);
this.storageServ.set('channelTitle', channel.title);
this.storageServ.set('channelId', JSON.stringify(channel));
this.channelMaintitle = channel.settingId.title;
this.storageServ.set('channelMaintitle', this.channelMaintitle);
this.router.navigate(
[`/platinum-chat/${this.selectedChat}/${'channel/' + channel.slug}`],
{ replaceUrl: true }
);
}
}
我在一个组件中按如下方式使用此聊天服务。 组件:
import { Component, OnInit } from '@angular/core';
import { ChatService } from 'src/app/services/chat.service';
@Component({
selector: 'app-platinum-chat',
templateUrl: './platinum-chat.page.html',
styleUrls: ['./platinum-chat.page.scss'],
})
export class PlatinumChatPage implements OnInit {
message: string;
authorId: any;
channel: any;
threadChat: any[] = [];
channelError: string = '';
constructor(
private chatService: ChatService,
) {
this.channel = localStorage.getItem('channelId');
}
ngOnInit() {
//load channel history
this.loadChannelHistory();
// get new message listener
this.chatService.getMessage().subscribe((msg: any) => {
this.threadChat.push(msg);
});
// error listener
this.chatService.getChannelError().subscribe(errorMessage => {
this.channelError = errorMessage;
});
//channel history listener
this.chatService.getChannelHistory().subscribe((threads: any) => {
this.threadChat = threads;
});
}
loadChannelHistory(){
const channelId = JSON.parse(this.channel);
this.chatService.loadChannelHistory(channelId && channelId._id || null);
}
onSendMessage() {
this.chatService.emitMessage(this.message, this.authorId, this.channel);
}
}
问题是,当我发送新消息时,它会多次调用“本地消息”事件侦听器。当我围绕这个问题进行调试时,根据一些建议,它是重复事件侦听器注册的问题。就我而言,我不会多次调用方法“establishSocketConnection”,然后它如何注册重复的侦听器(消息本地)并多次调用新消息。
注意:不是服务器端的问题。我只从服务器端发送单次发射,但它在前端被多次调用。
这里记录了我的问题 https://www.loom.com/share/d9142c73c6c54cb58802ac3edf704bc5
我不明白当前代码库有什么问题。
你们能帮我解决这个问题吗?提前致谢!!
无论其价值如何,都不要让订阅闲置。每次加载组件时都会添加一个订阅,但不会删除。这可能是,但可能不是原因。无论如何,我重构并实现了一种取消订阅()的方法,试一试。
@Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
})
export class PlatinumChatPage implements OnInit, OnDestroy {
message: string;
authorId: any;
channel: any;
threadChat: any[] = [];
channelError: string = '';
subs = [];
constructor(
private chatService: ChatService,
) {
this.channel = localStorage.getItem('channelId');
}
ngOnDestroy() {
this.subs.forEach(sub => sub.unsubscribe());
}
ngOnInit() {
//load channel history
this.loadChannelHistory();
// get new message listener
const chatSub = this.chatService.getMessage().subscribe((msg: any) => {
this.threadChat.push(msg);
});
// error listener
const errorSub = this.chatService.getChannelError().subscribe(errorMessage => {
this.channelError = errorMessage;
});
//channel history listener
const historySub = this.chatService.getChannelHistory().subscribe((threads: any) => {
this.threadChat = threads;
});
this.subs.push(chatSub, errorSub, historySub);
}
loadChannelHistory(){
const channelId = JSON.parse(this.channel);
this.chatService.loadChannelHistory(channelId && channelId._id || null);
}
onSendMessage() {
this.chatService.emitMessage(this.message, this.authorId, this.channel);
}
}