当我使用函数返回值并导致浏览器崩溃时,为什么* ngFor使无限循环?

时间:2019-08-23 16:06:30

标签: angular loops websocket ngfor

我不明白为什么* ngFor在使用函数返回值时进行无限循环。

我使用Angular 8

聊天视图HTML模板

<div class="row conpeek_message_chat_row" *ngFor="let chatMessage of chatMessages | async ">

   <div class="conpeek_chat_message_inner [innerHTML]="getChatMessageText(chatMessage)"></div>

</div>

聊天视图组件

private chatMessages:Observable<Array<any>>;

ngAfterViewInit() {
    this.chatMessages = this.chatMessagesService.getMessages();
}

getChatMessageText(chatMessage) {
    if (chatMessage.type == 'text' || chatMessage.type == 'info') {
      console.log('TYPE TEXT');
      return chatMessage.value;
    }
    if (chatMessage.type == 'image') {
      let img_obj = `<div class="conpeek_msg_img_outer"><img src="${chatMessage.value}"></div>`;
      return img_obj;
    }
    if (chatMessage.type == 'file') {
      let file_obj = `<a>${chatMessage.filename}</a>`;
      return file_obj;
    }
  }

聊天消息服务

private chatMessages = [];
private chatMessagesStream = new BehaviorSubject(this.chatMessages);

constructor(private dialogInfoService: DialogInfoService) {
    this.dialogInfoService.getDialogMembers().subscribe(members => {
      this.dialogMembers = members;
    });
}

getMessages(): Observable<any> {
   return this.chatMessagesStream.asObservable();
}

addMessage(chatMessage) {
    if (chatMessage.type != 'info') {
      for (let member of this.dialogMembers) {
        if (member.member_uuid == chatMessage.sender_dialog_uuid) {
          chatMessage.sender_name = member.member_presentation_name;
          chatMessage.avatar_id = member.avatar_id;

          if (member.member_uuid == this.dialogInfoService.getOwnDialogUUID()) {
            chatMessage.sender_type = 'contact';
            chatMessage.status = 'sending';
          } else {
            chatMessage.sender_type = 'consultant';
          }
        }
      }
    }

 this.chatMessages.push(chatMessage);
 this.chatMessagesStream.next(this.chatMessages);
}


初始化此组件并收到第一条消息后,我在控制台中收到消息console.log('TYPE TEXT');的无限循环

当我用它来请求图像消息类型下载blob时,我的chrome浏览器崩溃了。

为什么会发生,以及如何解决?


已编辑 我找到了为什么在这里有“无限”循环的线索。我用最少的附加代码重新创建了该应用程序,并且一切正常。但是,当我将WebSocket添加到我的应用程序中时,并且当此Webscoket进行一些操作(打开连接,发送消息,关闭连接等)时,我的循环进行了迭代。无限循环只是我的Websocket与服务器之间的乒乓消息,用于快速检测连接问题。

在这里,我显示问题,在WS进行每个操作后检查控制台日志,或者通过单击按钮发送ws msg。

我阅读了有关它的内容,每个更改检测事件(如按钮单击,http请求或websocket操作)都会导致此循环迭代。

https://stackblitz.com/edit/angular-xpb5hw

4 个答案:

答案 0 :(得分:1)

  1. 您尚未关闭课程报价
<div class="conpeek_chat_message_inner [innerHTML]="getChatMessageText(chatMessage)"></div>
  1. 您的chatMessagesService返回一个可观察到的消息,因此您需要订阅它
 // this.chatMessages = this.chatMessagesService.getMessages();
    this.chatMessagesService.getMessages().subscribe(messages=>{
      this.chatMessages=messages
    })
  1. 由于您的行为主题具有默认值,因此可以在初始化视图之前安全地进行订阅,以避免内容检查错误。将ngAfterViewInit()替换为ngOnInit()

我不确定您是如何测试addMessage函数的,但是在可观察范围内偶然有机会导致无限循环。

另外,一旦从服务接收到数据,这将是填充消息文本的好时机,而不是依赖html模板调用的函数。

这是最小的工作复制品:https://stackblitz.com/edit/angular-xnrupp

答案 1 :(得分:0)

您是否要更改chatMessagesgetChatMessageText(chatMessage)的值。如果是这样,它就是这个问题的根源

答案 2 :(得分:0)

尝试将其添加到您的组件中

chatMessageSubscription: Subscription;

    constructor(private chatMessageService: ChatMessagesService) {}

ngOnInit() {
    this.chatMessageSubscription = this.chatMessageService.chatMessagesStream .subscribe((messages: data) => {
            this.chatMessages = data;
        }
}

答案 3 :(得分:0)

我通过将*ngFor部分代码放入新组件并添加到该组件装饰器changeDetection: ChangeDetectionStrategy.OnPush选项中,解决了该问题。 初始化时具有停止检测更改(detach())的功能。现在,仅当来自服务的新消息到来时才循环迭代(我在下面的用户fn中添加了detachChanges)。

我需要隔离此组件,因为在Angular中,每个按钮的单击,请求或WebSocket消息都会触发整个应用程序中的检测更改,现在我可以对此进行控制。

ChatMessagesViewComponent

@Component({
  selector: 'chat-messages-view',
  template: `... *ngFor html here only ...`,
  styleUrls: ['./chat-messages-view.component.css'],
  changeDetection: ChangeDetectionStrategy.OnPush
})

constructor(private chatMessagesService: ChatMessagesService, private cd: ChangeDetectorRef) {
}

ngOnInit() {
  this.chatMessages = this.chatMessagesService.getMessages();
  this.cd.detach();
}

ngAfterViewInit() {
  this.chatMessagesService.getMessages().subscribe(messages => {
    this.cd.detectChanges();
    this.scrollMessagesToBottom(true);
  });
  this.scrollMessagesToBottom(false);
}