我们有一个Angular 2站点,其中有一个websocket,用于从后端到我们网格的数据。为了表明最近的更新,我们使用CSS来设置行的背景颜色,并在受影响的单元格上设置粗体字。
该指示应该只持续很短的时间。
1)我们的第一次尝试是在下一批从服务器到达时重置所有指标。这样做效果很好,但在某些观点中,很少有更新,这意味着指标可以保持很长时间,这有点令人困惑。
如果更新指标在固定的时间间隔后消失,例如4秒,那就会更加一致。
2)我们的下一次尝试是使用CSS动画。但过了一段时间,它已经落后很多了。给人的印象是,运行过多的动画会使浏览器超负荷,无法处理请求的时间。也许每个动画在后台都有自己的计时器?
3)第三次尝试是让一个计时器以固定间隔运行,然后检查要重置的记录。我们创建了一个定期检查到期项目的TimerService。将项添加到计时器池时,可以配置任意等待时间。
这样可行,但在日志窗口中经常出现违规警告:
[Violation] 'setInterval' handler took 56ms
[Violation] 'setInterval' handler took 74ms
[Violation] 'setInterval' handler took 63ms
[Violation] 'setInterval' handler took 88ms
...
但是当我们计算checkItems方法内部发生的事情时,它只需要0.03ms!
我们都有C#背景,并且刚刚与Angular合作了几个月。也许我们正在采用后端方法?
我们错过了上下文切换吗?
是否有其他更多前端友好方法?
我们可以对代码进行一些重要的优化吗?
所有建议都表示赞赏!
以下是建议的TimerService导致所有警告:
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) { }
}
修改
我尝试使用Observable.interval:完全相同的警告消息结果相同:“[Violation]'setInterval'处理程序占用xx ms”
我尝试使用setTimeout重复调用:结果相同,但修改了警告消息:“[Violation]'setTimeout'处理程序占用了xx ms”
我甚至试图清空每一行的checkItems,但仍然会收到警告。
警告是从zone.js中抛出的,似乎是一个Angular内部推荐。我知道我可以关闭Chrome开发者工具中的详细日志记录,但我经常使用console.debug进行开发,这意味着它们也会消失。
警告是可以的我想对于慢速函数,但在这种情况下它只是触发一个setInterval函数。为什么会这么慢?
答案 0 :(得分:1)
我会抛弃setInterval并仅在必要时使用setTimeout。
import { Injectable, OnInit } from "@angular/core";
import { Observable } from "rxjs/Rx";
import { Subject } from "rxjs/Subject";
let timer = null
@Injectable()
export class TimerService {
private timerItems: TimerItem[] = [];
private dueTimeReachedSubject: Subject<string> = new Subject<string>();
public dueTimeReached: Observable<string> = this.dueTimeReachedSubject.asObservable();
constructor() {
this.checkItems();
}
private checkItems() {
// clear the timeout
clearTimeout(timer)
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);
// only use the timer if there are timerItems
if(this.timerItems.length) {
timer = setTimeout(() => this.checkItems(), 1000)
}
}
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));
}
// check for items and engage the timer if necessary
this.checkItems()
}
public remove(key: string) {
this.timerItems = this.timerItems.filter(t => t.key !== key);
}
}
class TimerItem {
constructor(public key: string, public dueTime: number) { }
}
答案 1 :(得分:1)
据我了解,您希望CSS更改在一定时间间隔后消失。
.row-normal{
//css-styling goes here
}
.row-updates{
//css- here
}
---方法1 --- 在更新监听器上设置 在html中或通过在JavaScript代码中使用document.getElementByTagName来为视图定义onChange属性。
function onUpdate(){
let v = this;
v.className = "row-updated";
setTimeOut(function(){v.className = "row-normal"}, interval_in_millis)
}
希望这会有所帮助。
答案 2 :(得分:1)
在这样的电台中可以做很多事情。
第一个方法是将 ChangeDetectionStrategy 更改为 ChangeDetectionStrategy.onPush ,然后仅在需要时(在您的情况下,在checkItems末尾)激活检测,添加和删除。这将大大减少脚本编写,因为只有在被询问时,angular才需要评估您的html
您可以做的第二件事是检查您的太阳穴是否在 * ngIf 中有函数调用 或 * ngFor ,如果您执行angular操作,则将无法缓存此函数返回的值,并且必须为每次检查进行处理
您应该考虑的第三件事是优化checkItems,在O(n ^ 2)中运行checkItems并执行许多不必要的循环。你可以减少它
checkItems() {
this.timerItems = this.timerItems.filter(t => {
if(t.dueTime <= now){
this.dueTimeReachedSubject.next(t.key);
return false;
}
return true;
});
}
对于小数组,它不会有太大帮助,但越大的更改将开始生效。
我可以做更多的事情,但这三件事可以帮助我解决类似的性能问题
答案 3 :(得分:0)
确定从checkItems清除代码后,您确定要重新构建项目。
以防万一, 我确实设置了StackBlitz here
使用您的代码,但无法重新创建它。.
如果您仍然遇到问题,也许您可以派出StackBlitz并尝试在那里重新创建问题?
答案 4 :(得分:0)
一种解决方案是使用Angular's animations。
例如,添加到列表中的元素是:enter
transition(这是void => *
过渡的别名)的情况。从链接的文档中:
HTML:
<div @myInsertRemoveTrigger *ngIf="isShown" class="insert-remove-container">
<p>The box is inserted</p>
</div>
TS:
trigger('myInsertRemoveTrigger', [
transition(':enter', [
style({ opacity: 0 }),
animate('5s', style({ opacity: 1 })),
]),
transition(':leave', [
animate('5s', style({ opacity: 0 }))
])
]),
当带有myInsertRemoveTrigger
触发器的元素将“从空白中出来”(:enter
)时,它将最初获得样式opacity: 0
,然后过渡到opacity: 1
5秒钟。 / p>
animate()
的第一个参数(例如'5s'
)也可以用来定义delay and easing。您提到“新项目”样式可以应用4秒钟,因此过渡规则可以例如:
transition(':enter', [
style({ backgroundColor: 'green' }),
animate('1s 4s ease-in', style({ backgroundColor: 'white' })),
])
这将立即将绿色背景应用于新项目,然后在4秒钟后为它们提供白色背景色(过渡时间为1秒)。