Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Most effective way to apply frequent CSS changes in Angular 2

We have an Angular 2 site with a websocket pumping in data from backend to our grid. To indicate the recent updates we use CSS to set background color for the row and bold font on cells that are affected.

The indication should only last a short time.

1) Our first attempt was to reset all indicators when the next batch arrived from server. This was working well but in some views there are very seldom updates which means the indicators can stay very long which is kind of confusing.

It would be more consistent if update indicators disappeared after a fixed interval, like 4 seconds.

2) Our next attempt was to use CSS animations. But after a while it was lagging a lot. The impression is that too many animations running will overload the browser not coping with the requested times. Maybe each animation has its own timer in the background?

3) The third attempt is to have one timer running at fixed intervals and then checking what records to reset. We have created a TimerService that will regularly check for due items. When adding an item to the timer pool it can be configured with arbitrary wait time.

This works, but in the log window there are frequent violation warnings:

[Violation] 'setInterval' handler took 56ms
[Violation] 'setInterval' handler took 74ms
[Violation] 'setInterval' handler took 63ms
[Violation] 'setInterval' handler took 88ms
...

But when we time what happens inside the checkItems method it just takes 0.03ms!

  • We all have C# background and have just been working with Angular for some months. Maybe we are imposing a backend approach?

  • Is there a context switching going on that we have missed?

  • Is there an alternative more frontend friendly approach?

  • Are there some crucial optimizations we could do to our code?

All suggestions are appreciated!

Here is the suggested TimerService causing all warnings:

import { Injectable, OnInit } from "@angular/core";
import { Observable } from "rxjs/Rx";
import { Subject } from "rxjs/Subject";

@Injectable()
export class TimerService {
    private timerItems: TimerItem[] = [];
    private dueTimeReachedSubject: Subject<string> = new Subject<string>();
    public dueTimeReached: Observable<string> = this.dueTimeReachedSubject.asObservable();

    constructor() {
        setInterval(() => this.checkItems(), 1000);
    }

    private checkItems() {
        let now = Date.now();
        let removeKeys: string[] = [];
        this.timerItems.filter(t => t.dueTime <= now).forEach(t => {
            this.dueTimeReachedSubject.next(t.key);
            removeKeys.push(t.key);
        });
        this.timerItems = this.timerItems.filter(t => removeKeys.indexOf(t.key) < 0);
    }

    public add(key: string, delayInSeconds: number) {
        let dueTime = Date.now() + delayInSeconds * 1000;
        let timerItem = this.timerItems.find(t => t.key === key);
        if (timerItem) {
            timerItem.dueTime = dueTime;
        }
        else {
            this.timerItems.push(new TimerItem(key, dueTime));
        }
    }   

    public remove(key: string) {
        this.timerItems = this.timerItems.filter(t => t.key !== key);
    }
}


class TimerItem {
    constructor(public key: string, public dueTime: number) { }
}

EDIT

I tried to use Observable.interval: Same result with exactly the same warning message: "[Violation] 'setInterval' handler took xx ms"

I tried to use setTimeout with repeated calls: Same result but with modified warning message: "[Violation] 'setTimeout' handler took xx ms"

I even tried to empty the checkItems of every line and I still get the warnings.

The warning is thrown from within zone.js and seems to be an Angular inner recommendation. I know I can turn off verbose logging in Chrome developer tools, but I often use console.debug for development purposes and that means they will disappear as well.

A warning is OK I guess for slow functions, but in this case it is just triggering a setInterval function. Why would that be slow?

like image 520
Jakob Lithner Avatar asked May 18 '17 08:05

Jakob Lithner


1 Answers

there are numerous things that can be done in a stations like this.

the first one is to change the ChangeDetectionStrategy to ChangeDetectionStrategy.onPush , then active the detection only when needed, in your case at the end of checkItems ,add and remove . this will reduce dramatically the scripting as angular will need to evaluate your html only when asked

the second thing you can do is to check if your temple have a function call in an *ngIf or an *ngFor, if you do angular will not be able to cache the values this functions returns and will have to process them for each check

the third thing you should consider is optimize checkItems, checkItems runtime in O(n^2) and does many unnecessary loops. you can reduce it

checkItems() {
  this.timerItems = this.timerItems.filter(t => {
      if(t.dueTime <= now){
          this.dueTimeReachedSubject.next(t.key);
          return false;
      }
      return true;
  });        
}

for small arrays it wont help a lot but the bigger the get this change will start take effect..

i guss more things can be done but this 3 things help me in my similar performance issues

like image 82
Amit Wagner Avatar answered Oct 19 '22 05:10

Amit Wagner