Skip to content

Commit 5cc18f3

Browse files
committed
feat(shared/helpers): make Timeout pausable
1 parent cd75aaf commit 5cc18f3

2 files changed

Lines changed: 82 additions & 2 deletions

File tree

src/shared/__tests__/helpers.test.ts

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
getNextOccurrence,
88
toWalletAddressUrl,
99
setDifference,
10+
Timeout,
1011
} from '../helpers';
1112

1213
describe('objectEquals', () => {
@@ -165,3 +166,54 @@ describe('toWalletAddressUrl', () => {
165166
);
166167
});
167168
});
169+
170+
describe('Timeout', () => {
171+
jest.useFakeTimers();
172+
173+
let callback: jest.Mock;
174+
let timeout: Timeout;
175+
beforeEach(() => {
176+
callback = jest.fn();
177+
timeout = new Timeout(1000, callback);
178+
});
179+
180+
afterEach(() => {
181+
jest.clearAllTimers();
182+
test;
183+
});
184+
185+
it('should call the callback after the specified time', () => {
186+
jest.advanceTimersByTime(1000);
187+
expect(callback).toHaveBeenCalledTimes(1);
188+
});
189+
190+
it('should reset the timeout', () => {
191+
timeout.reset(2000);
192+
// @ts-expect-error for testing it's ok to access private properties
193+
expect(timeout.ms).toBe(2000);
194+
jest.advanceTimersByTime(2000);
195+
expect(callback).toHaveBeenCalledTimes(1);
196+
});
197+
198+
it('should pause the timeout', () => {
199+
timeout.pause();
200+
jest.advanceTimersByTime(1000);
201+
expect(callback).not.toHaveBeenCalled();
202+
});
203+
204+
it('should resume the timeout', () => {
205+
timeout.pause();
206+
jest.advanceTimersByTime(500);
207+
timeout.resume();
208+
jest.advanceTimersByTime(500);
209+
expect(callback).not.toHaveBeenCalled();
210+
jest.advanceTimersByTime(500);
211+
expect(callback).toHaveBeenCalledTimes(1);
212+
});
213+
214+
it('should clear the timeout', () => {
215+
timeout.clear();
216+
jest.advanceTimersByTime(1000);
217+
expect(callback).not.toHaveBeenCalled();
218+
});
219+
});

src/shared/helpers/time.ts

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -116,20 +116,48 @@ export function debounceSync<T extends unknown[], R>(
116116

117117
export class Timeout {
118118
private timeout: ReturnType<typeof setTimeout> | null = null;
119+
#ms: number;
120+
#isPaused = false;
121+
#remaining = 0;
122+
#startTime = 0;
123+
119124
constructor(
120125
ms: number,
121126
private callback: () => void,
122127
) {
123-
this.reset(ms);
128+
this.#ms = ms;
129+
if (ms > 0) this.reset(ms);
124130
}
125131

126132
reset(ms: number) {
127133
this.clear();
134+
this.#ms = ms;
135+
this.#isPaused = false;
136+
this.#startTime = Date.now();
128137
this.timeout = setTimeout(this.callback, ms);
129138
}
130139

140+
pause() {
141+
if (this.#isPaused) return;
142+
this.clear();
143+
this.#isPaused = true;
144+
this.#remaining = this.#ms - (Date.now() - this.#startTime);
145+
}
146+
147+
resume() {
148+
if (!this.#isPaused) return;
149+
if (this.#remaining > 0) {
150+
this.timeout = setTimeout(() => {
151+
this.callback();
152+
this.reset(this.#ms);
153+
}, this.#remaining);
154+
} else {
155+
this.reset(this.#ms);
156+
}
157+
}
158+
131159
clear() {
132-
if (this.timeout) {
160+
if (this.timeout !== null) {
133161
clearTimeout(this.timeout);
134162
this.timeout = null;
135163
}

0 commit comments

Comments
 (0)