From 12e1e422e301cad2f21006d4720883f6818f2a7f Mon Sep 17 00:00:00 2001 From: Samuel Sampson Date: Thu, 29 Jul 2021 19:35:14 +0000 Subject: [PATCH 1/9] Update RenderDebouncer to update screen readers once per second instead of once per animation frame. --- src/browser/RenderDebouncer.ts | 38 ++++++++++++++++++++++++---------- 1 file changed, 27 insertions(+), 11 deletions(-) diff --git a/src/browser/RenderDebouncer.ts b/src/browser/RenderDebouncer.ts index 2a06fdd66a..334d9a1ce8 100644 --- a/src/browser/RenderDebouncer.ts +++ b/src/browser/RenderDebouncer.ts @@ -5,6 +5,8 @@ import { IDisposable } from 'common/Types'; +const RENDER_DEBOUNCE_THRESHOLD_MS = 1000; // 1 Second + /** * Debounces calls to render terminal rows using animation frames. */ @@ -14,17 +16,17 @@ export class RenderDebouncer implements IDisposable { private _rowCount: number | undefined; private _animationFrame: number | undefined; + // The last moment that the Terminal was refreshed at + private _lastRefreshMs = 0; + // Whether a trailing refresh should be triggered due to a refresh request that was throttled + private _additionalRefreshRequested = false; + constructor( private _renderCallback: (start: number, end: number) => void ) { } - public dispose(): void { - if (this._animationFrame) { - window.cancelAnimationFrame(this._animationFrame); - this._animationFrame = undefined; - } - } + public dispose(): void {} public refresh(rowStart: number | undefined, rowEnd: number | undefined, rowCount: number): void { this._rowCount = rowCount; @@ -35,11 +37,25 @@ export class RenderDebouncer implements IDisposable { this._rowStart = this._rowStart !== undefined ? Math.min(this._rowStart, rowStart) : rowStart; this._rowEnd = this._rowEnd !== undefined ? Math.max(this._rowEnd, rowEnd) : rowEnd; - if (this._animationFrame) { - return; - } + // Only refresh if the time since last refresh is above a threshold, otherwise wait for + // enough time to pass before refreshing again. + const refreshRequestTime: number = Date.now(); + if (refreshRequestTime - this._lastRefreshMs >= RENDER_DEBOUNCE_THRESHOLD_MS) { + // Enough time has lapsed since the last refresh; refresh immediately + this._lastRefreshMs = refreshRequestTime; + this._innerRefresh(); + } else if (!this._additionalRefreshRequested) { + // This is the first additional request throttled; set up trailing refresh + const elapsed = refreshRequestTime - this._lastRefreshMs; + const waitPeriodBeforeTrailingRefresh = RENDER_DEBOUNCE_THRESHOLD_MS - elapsed; + this._additionalRefreshRequested = true; - this._animationFrame = window.requestAnimationFrame(() => this._innerRefresh()); + setTimeout(() => { + this._lastRefreshMs = Date.now(); + this._innerRefresh(); + this._additionalRefreshRequested = false; + }, waitPeriodBeforeTrailingRefresh); + } } private _innerRefresh(): void { @@ -55,9 +71,9 @@ export class RenderDebouncer implements IDisposable { // Reset debouncer (this happens before render callback as the render could trigger it again) this._rowStart = undefined; this._rowEnd = undefined; - this._animationFrame = undefined; // Run render callback this._renderCallback(start, end); } } + From e82f475c9bba34bf5f1e1a6406944eaf0765c1d1 Mon Sep 17 00:00:00 2001 From: Samuel Sampson Date: Thu, 29 Jul 2021 19:57:51 +0000 Subject: [PATCH 2/9] Separate the regular render debouncer from a Time-Based debouncer to be used to update Screen Readers --- src/browser/AccessibilityManager.ts | 6 +-- src/browser/RenderDebouncer.ts | 38 +++++---------- src/browser/TimeBasedDebouncer.ts | 76 +++++++++++++++++++++++++++++ 3 files changed, 90 insertions(+), 30 deletions(-) create mode 100644 src/browser/TimeBasedDebouncer.ts diff --git a/src/browser/AccessibilityManager.ts b/src/browser/AccessibilityManager.ts index c1ffc39a66..160aa3fccb 100644 --- a/src/browser/AccessibilityManager.ts +++ b/src/browser/AccessibilityManager.ts @@ -7,7 +7,7 @@ import * as Strings from 'browser/LocalizableStrings'; import { ITerminal } from 'browser/Types'; import { IBuffer } from 'common/buffer/Types'; import { isMac } from 'common/Platform'; -import { RenderDebouncer } from 'browser/RenderDebouncer'; +import { TimeBasedDebouncer } from 'browser/TimeBasedDebouncer'; import { addDisposableDomListener } from 'browser/Lifecycle'; import { Disposable } from 'common/Lifecycle'; import { ScreenDprMonitor } from 'browser/ScreenDprMonitor'; @@ -28,7 +28,7 @@ export class AccessibilityManager extends Disposable { private _liveRegion: HTMLElement; private _liveRegionLineCount: number = 0; - private _renderRowsDebouncer: RenderDebouncer; + private _renderRowsDebouncer: TimeBasedDebouncer; private _screenDprMonitor: ScreenDprMonitor; private _topBoundaryFocusListener: (e: FocusEvent) => void; @@ -72,7 +72,7 @@ export class AccessibilityManager extends Disposable { this._refreshRowsDimensions(); this._accessibilityTreeRoot.appendChild(this._rowContainer); - this._renderRowsDebouncer = new RenderDebouncer(this._renderRows.bind(this)); + this._renderRowsDebouncer = new TimeBasedDebouncer(this._renderRows.bind(this)); this._refreshRows(); this._liveRegion = document.createElement('div'); diff --git a/src/browser/RenderDebouncer.ts b/src/browser/RenderDebouncer.ts index 334d9a1ce8..2a06fdd66a 100644 --- a/src/browser/RenderDebouncer.ts +++ b/src/browser/RenderDebouncer.ts @@ -5,8 +5,6 @@ import { IDisposable } from 'common/Types'; -const RENDER_DEBOUNCE_THRESHOLD_MS = 1000; // 1 Second - /** * Debounces calls to render terminal rows using animation frames. */ @@ -16,17 +14,17 @@ export class RenderDebouncer implements IDisposable { private _rowCount: number | undefined; private _animationFrame: number | undefined; - // The last moment that the Terminal was refreshed at - private _lastRefreshMs = 0; - // Whether a trailing refresh should be triggered due to a refresh request that was throttled - private _additionalRefreshRequested = false; - constructor( private _renderCallback: (start: number, end: number) => void ) { } - public dispose(): void {} + public dispose(): void { + if (this._animationFrame) { + window.cancelAnimationFrame(this._animationFrame); + this._animationFrame = undefined; + } + } public refresh(rowStart: number | undefined, rowEnd: number | undefined, rowCount: number): void { this._rowCount = rowCount; @@ -37,25 +35,11 @@ export class RenderDebouncer implements IDisposable { this._rowStart = this._rowStart !== undefined ? Math.min(this._rowStart, rowStart) : rowStart; this._rowEnd = this._rowEnd !== undefined ? Math.max(this._rowEnd, rowEnd) : rowEnd; - // Only refresh if the time since last refresh is above a threshold, otherwise wait for - // enough time to pass before refreshing again. - const refreshRequestTime: number = Date.now(); - if (refreshRequestTime - this._lastRefreshMs >= RENDER_DEBOUNCE_THRESHOLD_MS) { - // Enough time has lapsed since the last refresh; refresh immediately - this._lastRefreshMs = refreshRequestTime; - this._innerRefresh(); - } else if (!this._additionalRefreshRequested) { - // This is the first additional request throttled; set up trailing refresh - const elapsed = refreshRequestTime - this._lastRefreshMs; - const waitPeriodBeforeTrailingRefresh = RENDER_DEBOUNCE_THRESHOLD_MS - elapsed; - this._additionalRefreshRequested = true; - - setTimeout(() => { - this._lastRefreshMs = Date.now(); - this._innerRefresh(); - this._additionalRefreshRequested = false; - }, waitPeriodBeforeTrailingRefresh); + if (this._animationFrame) { + return; } + + this._animationFrame = window.requestAnimationFrame(() => this._innerRefresh()); } private _innerRefresh(): void { @@ -71,9 +55,9 @@ export class RenderDebouncer implements IDisposable { // Reset debouncer (this happens before render callback as the render could trigger it again) this._rowStart = undefined; this._rowEnd = undefined; + this._animationFrame = undefined; // Run render callback this._renderCallback(start, end); } } - diff --git a/src/browser/TimeBasedDebouncer.ts b/src/browser/TimeBasedDebouncer.ts new file mode 100644 index 0000000000..8f444c4fe9 --- /dev/null +++ b/src/browser/TimeBasedDebouncer.ts @@ -0,0 +1,76 @@ +/** + * Copyright (c) 2018 The xterm.js authors. All rights reserved. + * @license MIT + */ + +const RENDER_DEBOUNCE_THRESHOLD_MS = 1000; // 1 Second + +/** + * Debounces calls to update screen readers to update at most once per second. + */ +export class TimeBasedDebouncer { + private _rowStart: number | undefined; + private _rowEnd: number | undefined; + private _rowCount: number | undefined; + + // The last moment that the Terminal was refreshed at + private _lastRefreshMs = 0; + // Whether a trailing refresh should be triggered due to a refresh request that was throttled + private _additionalRefreshRequested = false; + + constructor( + private _renderCallback: (start: number, end: number) => void + ) { + } + + public dispose(): void {} + + public refresh(rowStart: number | undefined, rowEnd: number | undefined, rowCount: number): void { + this._rowCount = rowCount; + // Get the min/max row start/end for the arg values + rowStart = rowStart !== undefined ? rowStart : 0; + rowEnd = rowEnd !== undefined ? rowEnd : this._rowCount - 1; + // Set the properties to the updated values + this._rowStart = this._rowStart !== undefined ? Math.min(this._rowStart, rowStart) : rowStart; + this._rowEnd = this._rowEnd !== undefined ? Math.max(this._rowEnd, rowEnd) : rowEnd; + + // Only refresh if the time since last refresh is above a threshold, otherwise wait for + // enough time to pass before refreshing again. + const refreshRequestTime: number = Date.now(); + if (refreshRequestTime - this._lastRefreshMs >= RENDER_DEBOUNCE_THRESHOLD_MS) { + // Enough time has lapsed since the last refresh; refresh immediately + this._lastRefreshMs = refreshRequestTime; + this._innerRefresh(); + } else if (!this._additionalRefreshRequested) { + // This is the first additional request throttled; set up trailing refresh + const elapsed = refreshRequestTime - this._lastRefreshMs; + const waitPeriodBeforeTrailingRefresh = RENDER_DEBOUNCE_THRESHOLD_MS - elapsed; + this._additionalRefreshRequested = true; + + setTimeout(() => { + this._lastRefreshMs = Date.now(); + this._innerRefresh(); + this._additionalRefreshRequested = false; + }, waitPeriodBeforeTrailingRefresh); + } + } + + private _innerRefresh(): void { + // Make sure values are set + if (this._rowStart === undefined || this._rowEnd === undefined || this._rowCount === undefined) { + return; + } + + // Clamp values + const start = Math.max(this._rowStart, 0); + const end = Math.min(this._rowEnd, this._rowCount - 1); + + // Reset debouncer (this happens before render callback as the render could trigger it again) + this._rowStart = undefined; + this._rowEnd = undefined; + + // Run render callback + this._renderCallback(start, end); + } +} + From 4a9ecb33bd73ffcab015eaf6768443ff9224d02e Mon Sep 17 00:00:00 2001 From: Samuel Sampson Date: Thu, 29 Jul 2021 19:58:59 +0000 Subject: [PATCH 3/9] Remove unneeded dispose method from TimeBasedDebouncer --- src/browser/TimeBasedDebouncer.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/browser/TimeBasedDebouncer.ts b/src/browser/TimeBasedDebouncer.ts index 8f444c4fe9..64226085a9 100644 --- a/src/browser/TimeBasedDebouncer.ts +++ b/src/browser/TimeBasedDebouncer.ts @@ -23,8 +23,6 @@ export class TimeBasedDebouncer { ) { } - public dispose(): void {} - public refresh(rowStart: number | undefined, rowEnd: number | undefined, rowCount: number): void { this._rowCount = rowCount; // Get the min/max row start/end for the arg values From 732927b0e225e598bcfaa8ea67b685c2776b1c43 Mon Sep 17 00:00:00 2001 From: Samuel Sampson Date: Thu, 29 Jul 2021 20:20:02 +0000 Subject: [PATCH 4/9] Add back IDisposable implementation in TimeBasedDebouncer for the ease of interoperability with RenderDebouncer --- src/browser/TimeBasedDebouncer.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/browser/TimeBasedDebouncer.ts b/src/browser/TimeBasedDebouncer.ts index 64226085a9..843787d5fd 100644 --- a/src/browser/TimeBasedDebouncer.ts +++ b/src/browser/TimeBasedDebouncer.ts @@ -5,10 +5,12 @@ const RENDER_DEBOUNCE_THRESHOLD_MS = 1000; // 1 Second +import { IDisposable } from 'common/Types'; + /** * Debounces calls to update screen readers to update at most once per second. */ -export class TimeBasedDebouncer { +export class TimeBasedDebouncer implements IDisposable { private _rowStart: number | undefined; private _rowEnd: number | undefined; private _rowCount: number | undefined; @@ -23,6 +25,8 @@ export class TimeBasedDebouncer { ) { } + public dispose(): void {} + public refresh(rowStart: number | undefined, rowEnd: number | undefined, rowCount: number): void { this._rowCount = rowCount; // Get the min/max row start/end for the arg values From f1849cafafe01df5f153aefc02f2eb8d03325148 Mon Sep 17 00:00:00 2001 From: Samuel Sampson Date: Tue, 17 Aug 2021 20:48:31 +0000 Subject: [PATCH 5/9] Introduce IRenderDebouncer interface, unifying RenderDebouncer and TimeBasedDebouncer types --- src/browser/AccessibilityManager.ts | 4 ++-- src/browser/RenderDebouncer.ts | 4 ++-- src/browser/TimeBasedDebouncer.ts | 4 ++-- src/browser/Types.d.ts | 4 ++++ src/browser/services/RenderService.ts | 4 ++-- 5 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/browser/AccessibilityManager.ts b/src/browser/AccessibilityManager.ts index 160aa3fccb..1be3342d73 100644 --- a/src/browser/AccessibilityManager.ts +++ b/src/browser/AccessibilityManager.ts @@ -4,7 +4,7 @@ */ import * as Strings from 'browser/LocalizableStrings'; -import { ITerminal } from 'browser/Types'; +import { ITerminal, IRenderDebouncer } from 'browser/Types'; import { IBuffer } from 'common/buffer/Types'; import { isMac } from 'common/Platform'; import { TimeBasedDebouncer } from 'browser/TimeBasedDebouncer'; @@ -28,7 +28,7 @@ export class AccessibilityManager extends Disposable { private _liveRegion: HTMLElement; private _liveRegionLineCount: number = 0; - private _renderRowsDebouncer: TimeBasedDebouncer; + private _renderRowsDebouncer: IRenderDebouncer; private _screenDprMonitor: ScreenDprMonitor; private _topBoundaryFocusListener: (e: FocusEvent) => void; diff --git a/src/browser/RenderDebouncer.ts b/src/browser/RenderDebouncer.ts index 2a06fdd66a..025210707c 100644 --- a/src/browser/RenderDebouncer.ts +++ b/src/browser/RenderDebouncer.ts @@ -3,12 +3,12 @@ * @license MIT */ -import { IDisposable } from 'common/Types'; +import { IRenderDebouncer } from 'browser/Types'; /** * Debounces calls to render terminal rows using animation frames. */ -export class RenderDebouncer implements IDisposable { +export class RenderDebouncer implements IRenderDebouncer { private _rowStart: number | undefined; private _rowEnd: number | undefined; private _rowCount: number | undefined; diff --git a/src/browser/TimeBasedDebouncer.ts b/src/browser/TimeBasedDebouncer.ts index 843787d5fd..e4aeb387dc 100644 --- a/src/browser/TimeBasedDebouncer.ts +++ b/src/browser/TimeBasedDebouncer.ts @@ -5,12 +5,12 @@ const RENDER_DEBOUNCE_THRESHOLD_MS = 1000; // 1 Second -import { IDisposable } from 'common/Types'; +import { IRenderDebouncer } from 'browser/Types'; /** * Debounces calls to update screen readers to update at most once per second. */ -export class TimeBasedDebouncer implements IDisposable { +export class TimeBasedDebouncer implements IRenderDebouncer { private _rowStart: number | undefined; private _rowEnd: number | undefined; private _rowCount: number | undefined; diff --git a/src/browser/Types.d.ts b/src/browser/Types.d.ts index c268c7bf86..0d74b39faa 100644 --- a/src/browser/Types.d.ts +++ b/src/browser/Types.d.ts @@ -309,3 +309,7 @@ export interface ICharacterJoiner { id: number; handler: CharacterJoinerHandler; } + +export interface IRenderDebouncer extends IDisposable { + refresh(rowStart: number | undefined, rowEnd: number | undefined, rowCount: number): void; +} diff --git a/src/browser/services/RenderService.ts b/src/browser/services/RenderService.ts index fc2eb43540..332e71dae6 100644 --- a/src/browser/services/RenderService.ts +++ b/src/browser/services/RenderService.ts @@ -9,7 +9,7 @@ import { EventEmitter, IEvent } from 'common/EventEmitter'; import { Disposable } from 'common/Lifecycle'; import { ScreenDprMonitor } from 'browser/ScreenDprMonitor'; import { addDisposableDomListener } from 'browser/Lifecycle'; -import { IColorSet } from 'browser/Types'; +import { IColorSet, IRenderDebouncer } from 'browser/Types'; import { IOptionsService, IBufferService } from 'common/services/Services'; import { ICharSizeService, IRenderService } from 'browser/services/Services'; @@ -22,7 +22,7 @@ interface ISelectionState { export class RenderService extends Disposable implements IRenderService { public serviceBrand: undefined; - private _renderDebouncer: RenderDebouncer; + private _renderDebouncer: IRenderDebouncer; private _screenDprMonitor: ScreenDprMonitor; private _isPaused: boolean = false; From 9f24cf7e72a729946e606105b48f12dd953fad5a Mon Sep 17 00:00:00 2001 From: Samuel Sampson Date: Tue, 17 Aug 2021 21:28:58 +0000 Subject: [PATCH 6/9] Cancel queued refreshes in TimeBasedDebouncer on dispose --- src/browser/TimeBasedDebouncer.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/browser/TimeBasedDebouncer.ts b/src/browser/TimeBasedDebouncer.ts index e4aeb387dc..455621d7f0 100644 --- a/src/browser/TimeBasedDebouncer.ts +++ b/src/browser/TimeBasedDebouncer.ts @@ -20,12 +20,18 @@ export class TimeBasedDebouncer implements IRenderDebouncer { // Whether a trailing refresh should be triggered due to a refresh request that was throttled private _additionalRefreshRequested = false; + private _refreshTimeoutID: number | undefined; + constructor( private _renderCallback: (start: number, end: number) => void ) { } - public dispose(): void {} + public dispose(): void { + if (this._refreshTimeoutID) { + clearTimeout(this._refreshTimeoutID); + } + } public refresh(rowStart: number | undefined, rowEnd: number | undefined, rowCount: number): void { this._rowCount = rowCount; @@ -49,7 +55,7 @@ export class TimeBasedDebouncer implements IRenderDebouncer { const waitPeriodBeforeTrailingRefresh = RENDER_DEBOUNCE_THRESHOLD_MS - elapsed; this._additionalRefreshRequested = true; - setTimeout(() => { + this._refreshTimeoutID = window.setTimeout(() => { this._lastRefreshMs = Date.now(); this._innerRefresh(); this._additionalRefreshRequested = false; From ae58302dfaba3ffa2a8db74f88e728e8c875db2f Mon Sep 17 00:00:00 2001 From: Samuel Sampson Date: Tue, 17 Aug 2021 21:36:47 +0000 Subject: [PATCH 7/9] Allow render debounce interval to be configured at construction time in TimeBasedDebouncer --- src/browser/TimeBasedDebouncer.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/browser/TimeBasedDebouncer.ts b/src/browser/TimeBasedDebouncer.ts index 455621d7f0..584b33ce97 100644 --- a/src/browser/TimeBasedDebouncer.ts +++ b/src/browser/TimeBasedDebouncer.ts @@ -8,7 +8,7 @@ const RENDER_DEBOUNCE_THRESHOLD_MS = 1000; // 1 Second import { IRenderDebouncer } from 'browser/Types'; /** - * Debounces calls to update screen readers to update at most once per second. + * Debounces calls to update screen readers to update at most once configurable interval of time. */ export class TimeBasedDebouncer implements IRenderDebouncer { private _rowStart: number | undefined; @@ -23,7 +23,8 @@ export class TimeBasedDebouncer implements IRenderDebouncer { private _refreshTimeoutID: number | undefined; constructor( - private _renderCallback: (start: number, end: number) => void + private _renderCallback: (start: number, end: number) => void, + private readonly debounceThresholdMS = RENDER_DEBOUNCE_THRESHOLD_MS ) { } @@ -45,14 +46,14 @@ export class TimeBasedDebouncer implements IRenderDebouncer { // Only refresh if the time since last refresh is above a threshold, otherwise wait for // enough time to pass before refreshing again. const refreshRequestTime: number = Date.now(); - if (refreshRequestTime - this._lastRefreshMs >= RENDER_DEBOUNCE_THRESHOLD_MS) { + if (refreshRequestTime - this._lastRefreshMs >= this.debounceThresholdMS) { // Enough time has lapsed since the last refresh; refresh immediately this._lastRefreshMs = refreshRequestTime; this._innerRefresh(); } else if (!this._additionalRefreshRequested) { // This is the first additional request throttled; set up trailing refresh const elapsed = refreshRequestTime - this._lastRefreshMs; - const waitPeriodBeforeTrailingRefresh = RENDER_DEBOUNCE_THRESHOLD_MS - elapsed; + const waitPeriodBeforeTrailingRefresh = this.debounceThresholdMS - elapsed; this._additionalRefreshRequested = true; this._refreshTimeoutID = window.setTimeout(() => { From e778df672acd8098c8bfd0adae5600420fc65fa3 Mon Sep 17 00:00:00 2001 From: Samuel Sampson Date: Tue, 17 Aug 2021 22:20:04 +0000 Subject: [PATCH 8/9] Don't clear timeouts in TimeBasedDebouncer if they've already been executed. --- src/browser/TimeBasedDebouncer.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/browser/TimeBasedDebouncer.ts b/src/browser/TimeBasedDebouncer.ts index 584b33ce97..03e1a0c248 100644 --- a/src/browser/TimeBasedDebouncer.ts +++ b/src/browser/TimeBasedDebouncer.ts @@ -60,6 +60,7 @@ export class TimeBasedDebouncer implements IRenderDebouncer { this._lastRefreshMs = Date.now(); this._innerRefresh(); this._additionalRefreshRequested = false; + this._refreshTimeoutID = undefined; // No longer need to clear the timeout }, waitPeriodBeforeTrailingRefresh); } } From 42e0fcda29d2f202feb9b8ef6318eb1adb421d5a Mon Sep 17 00:00:00 2001 From: Samuel Sampson Date: Tue, 17 Aug 2021 22:26:14 +0000 Subject: [PATCH 9/9] Fix lint error due to private variable --- src/browser/TimeBasedDebouncer.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/browser/TimeBasedDebouncer.ts b/src/browser/TimeBasedDebouncer.ts index 03e1a0c248..707e25cbb7 100644 --- a/src/browser/TimeBasedDebouncer.ts +++ b/src/browser/TimeBasedDebouncer.ts @@ -24,7 +24,7 @@ export class TimeBasedDebouncer implements IRenderDebouncer { constructor( private _renderCallback: (start: number, end: number) => void, - private readonly debounceThresholdMS = RENDER_DEBOUNCE_THRESHOLD_MS + private readonly _debounceThresholdMS = RENDER_DEBOUNCE_THRESHOLD_MS ) { } @@ -46,14 +46,14 @@ export class TimeBasedDebouncer implements IRenderDebouncer { // Only refresh if the time since last refresh is above a threshold, otherwise wait for // enough time to pass before refreshing again. const refreshRequestTime: number = Date.now(); - if (refreshRequestTime - this._lastRefreshMs >= this.debounceThresholdMS) { + if (refreshRequestTime - this._lastRefreshMs >= this._debounceThresholdMS) { // Enough time has lapsed since the last refresh; refresh immediately this._lastRefreshMs = refreshRequestTime; this._innerRefresh(); } else if (!this._additionalRefreshRequested) { // This is the first additional request throttled; set up trailing refresh const elapsed = refreshRequestTime - this._lastRefreshMs; - const waitPeriodBeforeTrailingRefresh = this.debounceThresholdMS - elapsed; + const waitPeriodBeforeTrailingRefresh = this._debounceThresholdMS - elapsed; this._additionalRefreshRequested = true; this._refreshTimeoutID = window.setTimeout(() => {