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);
    }
  }