mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
fix(forms): clean up abort listener after timeout
Removes the abort event listener once the debounce timeout completes.
This avoids lingering listeners, prevents potential memory leaks, and ensures
the abort logic runs at most once.
(cherry picked from commit e7d99f02cb)
This commit is contained in:
@@ -42,8 +42,18 @@ export function debounce<TValue, TPathKind extends PathKind = PathKind.Root>(
|
||||
function debounceForDuration(durationInMilliseconds: number): Debouncer<unknown> {
|
||||
return (_context, abortSignal) => {
|
||||
return new Promise((resolve) => {
|
||||
const timeoutId = setTimeout(resolve, durationInMilliseconds);
|
||||
abortSignal.addEventListener('abort', () => clearTimeout(timeoutId));
|
||||
let timeoutId: ReturnType<typeof setTimeout> | undefined;
|
||||
|
||||
const onAbort = () => {
|
||||
clearTimeout(timeoutId);
|
||||
};
|
||||
|
||||
timeoutId = setTimeout(() => {
|
||||
abortSignal.removeEventListener('abort', onAbort);
|
||||
resolve();
|
||||
}, durationInMilliseconds);
|
||||
|
||||
abortSignal.addEventListener('abort', onAbort, {once: true});
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
@@ -237,6 +237,34 @@ describe('debounce', () => {
|
||||
expect(abortSpy).toHaveBeenCalledTimes(1);
|
||||
expect(street.value()).toBe('1600 Amphitheatre Pkwy');
|
||||
});
|
||||
|
||||
it('should remove abort listener when debounce completes', async () => {
|
||||
const addListenerSpy = spyOn(AbortSignal.prototype, 'addEventListener').and.callThrough();
|
||||
const removeListenerSpy = spyOn(
|
||||
AbortSignal.prototype,
|
||||
'removeEventListener',
|
||||
).and.callThrough();
|
||||
|
||||
const address = signal({street: ''});
|
||||
const addressForm = form(
|
||||
address,
|
||||
(address) => {
|
||||
debounce(address.street, 1);
|
||||
},
|
||||
options(),
|
||||
);
|
||||
const street = addressForm.street();
|
||||
|
||||
street.setControlValue('1600 Amphitheatre Pkwy');
|
||||
expect(addListenerSpy).toHaveBeenCalledOnceWith('abort', jasmine.any(Function), {
|
||||
once: true,
|
||||
});
|
||||
expect(removeListenerSpy).not.toHaveBeenCalled();
|
||||
|
||||
await timeout(10);
|
||||
expect(street.value()).toBe('1600 Amphitheatre Pkwy');
|
||||
expect(removeListenerSpy).toHaveBeenCalledOnceWith('abort', jasmine.any(Function));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user