代码之家  ›  专栏  ›  技术社区  ›  chuckd

使用间隔和计算日期时间倒计时正在扼杀我在谷歌Chrome中的CPU

  •  0
  • chuckd  · 技术社区  · 4 年前

    我用我的Angular应用程序在屏幕上显示日期/时间倒计时。这是从特定日期和时间开始的倒计时。

    用谷歌浏览器每秒钟运行一次都会消耗掉我的CPU。 可能是moment()lib有一些效果,但还不确定。 我可能会立即尝试,看看这是否有帮助。 enter image description here

    有更好(更有效)的方法吗?

    这是我的代码

    calculateTimeRemaining(endDate: Date, timeZone: string, eventId: number) {
      setInterval(() => {
        const element = document.getElementById("event-" + eventId);
        if (element) {
          const currentDateUtc = moment().utc();
          const endDateUtc = moment(endDate).utc(true);
          endDateUtc.tz(timeZone);
          const dif = moment.duration(endDateUtc.diff(currentDateUtc));
          const hours = dif.hours();
          const minutes = dif.minutes();
          const seconds = dif.seconds();
          if (hours < 1) {
            element.innerHTML = minutes + ' m ' + seconds + ' s';
          } else {
            element.innerHTML = hours + ' h ' + minutes + ' m ' + seconds + ' s';
          }
        }
      }, 1000);
    }
    <span id="event-{{event.id}}" class="float-right yb-color">{{calculateTimeRemaining(event.datePendingUtc, event.yogabandTimeZoneId, event.id)}}</span>

    EDIT(解决方案) -我已经在管道中实现了一个倒计时计时器(按照建议)来解决这个问题。我将在下面包含管道的完整代码以及我如何调用它。我还有一个服务,用于在计时器结束时向我的组件发送通知,以便我可以执行其他操作。

    import { Pipe, PipeTransform } from '@angular/core';
    import { Observable, of, timer } from 'rxjs';
    import { map, takeWhile } from 'rxjs/operators';
    import { EventService } from 'src/app/core/services/event.service';
    
    @Pipe({
      name: 'timeRemaining'
    })
    export class TimeRemainingPipe implements PipeTransform {
      eventId: number;
      expired = false;
    
      constructor(private eventService: EventService) {}
    
      /**
       * @param futureDate    should be in a valid Date Time format
       *                      e.g. YYYY-MM-DDTHH:mm:ss.msz
       *                      e.g. 2021-10-06T17:27:10.740z
       */
       public transform(futureDateUtc: string, eventId: number): Observable<string> {
        
        this.eventId = eventId;
        /**
         * Initial check to see if time remaining is in the future
         * If not, don't bother creating an observable
         */
    
        if (!futureDateUtc || this.getMsDiff(futureDateUtc) < 0) {
          console.info('Pipe - Time Expired Event: ' + eventId);
          return of('EXPIRED');
        }
        
        return timer(0, 1000).pipe(
            takeWhile(() => !this.expired),
            map(() => {
                return this.msToTime(this.getMsDiff(futureDateUtc));
            })
        );
      }
    
      /**
       * Gets the millisecond difference between a future date and now
       * @private
       * @param   futureDateUtc: string
       * @returns number  milliseconds remaining
       */
       // Z converts to local time
       private getMsDiff = (futureDate: string): number => (+(new Date(futureDate + 'Z')) - Date.now());
    
    
       /**
        * Converts milliseconds to the
        *
        * @private
        * @param msRemaining
        * @returns null    when no time is remaining
        *          string  in the format `HH:mm:ss`
        */
       private msToTime(msRemaining: number): string | null {
           if (msRemaining < 0) {
               console.info('Pipe - No Time Remaining:', msRemaining);
               this.expired = true;
               this.eventService.expired(this.eventId);
               return 'EXPIRED';
           }
    
           let seconds: string | number = Math.floor((msRemaining / 1000) % 60),
               minutes: string | number = Math.floor((msRemaining / (1000 * 60)) % 60),
               hours: string | number = Math.floor((msRemaining / (1000 * 60 * 60)) % 24);
    
           /**
            * Add the relevant `0` prefix if any of the numbers are less than 10
            * i.e. 5 -> 05
            */
           seconds = (seconds < 10) ? '0' + seconds : seconds;
           minutes = (minutes < 10) ? '0' + minutes : minutes;
           hours = (hours < 10) ? '0' + hours : hours;
    
           return `${hours}:${minutes}:${seconds}`;
       }
    
    }
    <span class="float-right yb-color">{{(event.dateUtc | timeRemaining: event.id | async}}</span>

    这是服务

    export class EventService {
      private subject = new Subject <any> ();
    
      expired(eventId: number) {
        this.subject.next(eventId);
      }
    
      expiredEvents(): Observable <any> {
        return this.subject.asObservable();
      }
    }
    
    // how it's called in the component
    
    this.eventService.expiredEvents().subscribe(eventId => {
      console.info('Service - Expired Event:', eventId);
      // do something now w/ expired event
    });
    1 回复  |  直到 4 年前
        1
  •  1
  •   deerawan    4 年前

    为了提高性能,不要调用Angular模板中的任何函数,因为它将在每次更改检测中运行。

    这篇文章很好地描述了这个问题 https://medium.com/showpad-engineering/why-you-should-never-use-function-calls-in-angular-template-expressions-e1a50f9c0496

    我们可以使用纯管道更改您的实现

    time-remaining.pipe.ts

    @Pipe({
      name: 'timeRemaining',
    })
    export class TimeRemainingPipe {
      transform(event: any): any {
        // put implementation here
        // return time remaining
      }
    }
    

    我相信管道,你可以移除 const element = document.getElementById("event-" + eventId); 部分

    component.html

    <span id="event-{{event.id}}" class="float-right yb-color">{{ event | timeRemaining }}</span>
    

    参考 https://angular.io/guide/pipes