Angular Freezing With Websocket When Browser Tab Doesn't Have Focus

1.1k Views Asked by At

We've got an Angular 8 project that has a websocket with a constant stream of data coming in. The data is stored in a clientInfo object and the changes are reflected in the page instantly with a {{ service.clientInfo.variable }}. The only issue is that when I go to another tab in the browser and come back to the page, it sometimes takes a few seconds for the page to catch up and everything is unresponsive until then.

If I leave the browser tab inactive for longer, all my components are hidden and the entire tab freezes permanently and I have to reopen it (chrome). I noticed that it doesn't happen if I go to another part of the site that doesn't show the data from the websocket. To me this seems like the change detection is buffering everything and not making any DOM changes when the browser tab doesn't have focus, eventually running out of memory.

I thought I could maybe try to use the document's "visibilitychange" event to detect when a browser tab is hidden, then set a show/hide boolean value. I tried to use an *ngIf with that flag to hide the section that shows the websocket data, but when the tab loses focus, the ngIf doesn't hide it in time.

Are there any simple solutions for this? Can I somehow tell Angular to stop change detection on the clientInfo object when the tab isn't active? I thought I could use the previously mentioned visibility flag to store incoming websocket data in a different object and then when the tab regains focus, I could merge those values with the main clientInfo object, but I'm sure there must be a simpler solution.

Edit for a bit more clarity on how it's set up:

Here's a simplified version of the clientInfo object which is in our service, there's more to it but the streams are the part that's shown on the page in question:

clientInfo = {
  streams: [
    { id: 1, count: 123 },
    { id: 2, count: 456 }
  ]
};

The page then shows this data (again simplified) like this:

<li *ngFor="let stream of service.streams">
  {{ stream.count }}
</li>

The streams value is a getter in our service which is set like this:

get streams(): DataStream[] {
  if(this.clientInfo) {
    return this.clientInfo.streams;
  }
  return [];
}

The reason we use a getter is because the path is actually longer than clientInfo.streams, I've just written it like this for the sake of simplicity.

The websocket sends delta updates so we loop through the streams, check if the id matches, and update the counts.

Thanks in advance, Chris.

1

There are 1 best solutions below

0
On BEST ANSWER

Apologies, I guess I simplified things too much in my question. I left out the ngx-charts (https://github.com/swimlane/ngx-charts) component that I was using in the ngFor loop. When I commented out the code that updates the graph data within my clientInfo object, the page stopped freezing.

ngx-charts must have been asking d3 to update the graph over and over again but without the tab being focused. Not that many people will be doing this but I guess the takeaway is, don't update ngx-charts when nobody can see your tab.