Skip to content

Commit b7ae986

Browse files
committed
use 2-timers approach and adjust tests
1 parent 584f294 commit b7ae986

File tree

6 files changed

+275
-183
lines changed

6 files changed

+275
-183
lines changed

packages/replay/src/replay.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/* eslint-disable max-lines */ // TODO: We might want to split this file up
22
import { addGlobalEventProcessor, captureException, getCurrentHub, setContext } from '@sentry/core';
33
import { Breadcrumb, Event } from '@sentry/types';
4-
import { addInstrumentationHandler, debounce, logger } from '@sentry/utils';
4+
import { addInstrumentationHandler, logger } from '@sentry/utils';
55
import { EventType, record } from 'rrweb';
66

77
import {
@@ -44,6 +44,7 @@ import { createBreadcrumb } from './util/createBreadcrumb';
4444
import { createPayload } from './util/createPayload';
4545
import { createPerformanceSpans } from './util/createPerformanceSpans';
4646
import { createReplayEnvelope } from './util/createReplayEnvelope';
47+
import { debounce } from './util/debounce';
4748
import { getReplayEvent } from './util/getReplayEvent';
4849
import { isExpired } from './util/isExpired';
4950
import { isSessionExpired } from './util/isSessionExpired';
@@ -872,6 +873,7 @@ export class ReplayContainer implements ReplayContainerInterface {
872873
// It's possible there are other flush requests queued and waiting for it
873874
// to resolve. We want to reduce all outstanding requests (as well as any
874875
// new flush requests that occur within a second of the locked flush
876+
// completing) into a single flush.thin a second of the locked flush
875877
// completing) into a single flush.
876878

877879
try {

packages/utils/src/debounce.ts renamed to packages/replay/src/util/debounce.ts

Lines changed: 21 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -26,72 +26,45 @@ type DebounceOptions = { maxWait?: number };
2626
*/
2727
export function debounce(func: CallbackFunction, wait: number, options?: DebounceOptions): DebouncedCallback {
2828
let callbackReturnValue: unknown;
29-
let timerId: ReturnType<typeof setTimeout> | undefined;
30-
let lastCallTime: number | undefined;
31-
let lastInvokeTime = 0;
3229

33-
const maxWait = options && options.maxWait ? Math.max(options.maxWait || 0, wait) : 0;
30+
let timerId: ReturnType<typeof setTimeout> | undefined;
31+
let maxTimerId: ReturnType<typeof setTimeout> | undefined;
3432

35-
function invokeFunc(time: number): unknown {
36-
timerId = undefined;
37-
38-
// Only invoke if we have `lastCallTime` which means `func` has been
39-
// debounced at least once.
40-
if (lastCallTime !== undefined) {
41-
lastInvokeTime = time;
42-
callbackReturnValue = func();
43-
}
33+
const maxWait = options && options.maxWait ? Math.max(options.maxWait, wait) : 0;
4434

35+
function invokeFunc(): unknown {
36+
cancelTimers();
37+
callbackReturnValue = func();
4538
return callbackReturnValue;
4639
}
4740

48-
function calcRemainingWait(time: number): number {
49-
const timeSinceLastCall = time - (lastCallTime || 0);
50-
const timeSinceLastInvoke = time - lastInvokeTime;
51-
const remainingWait = wait - timeSinceLastCall;
52-
53-
return maxWait ? Math.min(remainingWait, maxWait - timeSinceLastInvoke) : remainingWait;
41+
function cancelTimers(): void {
42+
timerId !== undefined && clearTimeout(timerId);
43+
maxTimerId !== undefined && clearTimeout(maxTimerId);
44+
timerId = maxTimerId = undefined;
5445
}
5546

56-
function shouldInvoke(time: number): boolean {
57-
const timeSinceLastCall = time - (lastCallTime || 0);
58-
const timeSinceLastInvoke = time - lastInvokeTime;
59-
60-
return timeSinceLastCall >= wait || (Boolean(maxWait) && timeSinceLastInvoke >= maxWait);
61-
}
62-
63-
function timerExpired(): void {
64-
const time = Date.now();
65-
if (shouldInvoke(time)) {
66-
return void invokeFunc(time);
47+
function flush(): unknown {
48+
if (timerId !== undefined || maxTimerId !== undefined) {
49+
return invokeFunc();
6750
}
68-
69-
// Restart the timer.
70-
timerId = setTimeout(timerExpired, calcRemainingWait(time));
51+
return callbackReturnValue;
7152
}
7253

73-
function cancel(): void {
74-
if (timerId !== undefined) {
54+
function debounced(): unknown {
55+
if (timerId) {
7556
clearTimeout(timerId);
7657
}
77-
lastInvokeTime = 0;
78-
lastCallTime = timerId = undefined;
79-
}
58+
timerId = setTimeout(invokeFunc, wait);
8059

81-
function flush(): unknown {
82-
return timerId === undefined ? callbackReturnValue : invokeFunc(Date.now());
83-
}
84-
85-
function debounced(): unknown {
86-
lastCallTime = Date.now();
87-
if (timerId === undefined) {
88-
lastInvokeTime = lastCallTime;
89-
timerId = setTimeout(timerExpired, wait);
60+
if (maxWait && maxTimerId === undefined) {
61+
maxTimerId = setTimeout(invokeFunc, maxWait);
9062
}
63+
9164
return callbackReturnValue;
9265
}
9366

94-
debounced.cancel = cancel;
67+
debounced.cancel = cancelTimers;
9568
debounced.flush = flush;
9669
return debounced;
9770
}

packages/replay/test/unit/index-errorSampleRate.test.ts

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ describe('Replay (errorSampleRate)', () => {
5454
expect(replay).not.toHaveLastSentReplay();
5555

5656
captureException(new Error('testing'));
57-
jest.runAllTimers();
57+
jest.advanceTimersByTime(5000);
5858
await new Promise(process.nextTick);
5959

6060
expect(replay).toHaveSentReplay({
@@ -99,8 +99,7 @@ describe('Replay (errorSampleRate)', () => {
9999
events: JSON.stringify([{ data: { isCheckout: true }, timestamp: BASE_TIMESTAMP + 5020, type: 2 }]),
100100
});
101101

102-
jest.runAllTimers();
103-
await new Promise(process.nextTick);
102+
jest.advanceTimersByTime(5000);
104103

105104
// New checkout when we call `startRecording` again after uploading segment
106105
// after an error occurs
@@ -118,8 +117,10 @@ describe('Replay (errorSampleRate)', () => {
118117
domHandler({
119118
name: 'click',
120119
});
121-
jest.runAllTimers();
120+
121+
jest.advanceTimersByTime(5000);
122122
await new Promise(process.nextTick);
123+
123124
expect(replay).toHaveLastSentReplay({
124125
events: JSON.stringify([
125126
{
@@ -297,7 +298,7 @@ describe('Replay (errorSampleRate)', () => {
297298

298299
captureException(new Error('testing'));
299300

300-
jest.runAllTimers();
301+
jest.advanceTimersByTime(5000);
301302
await new Promise(process.nextTick);
302303

303304
expect(replay).toHaveSentReplay({
@@ -398,7 +399,8 @@ it('sends a replay after loading the session multiple times', async () => {
398399
expect(replay).not.toHaveLastSentReplay();
399400

400401
captureException(new Error('testing'));
401-
jest.runAllTimers();
402+
403+
jest.advanceTimersByTime(5000);
402404
await new Promise(process.nextTick);
403405

404406
expect(replay).toHaveSentReplay({

0 commit comments

Comments
 (0)