-
Notifications
You must be signed in to change notification settings - Fork 50
feat(common): use addEventListener
instead of onabort
in async utils onAbortPromise
#645
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
feat(common): use addEventListener
instead of onabort
in async utils onAbortPromise
#645
Conversation
🦋 Changeset detectedLatest commit: 74a9a95 The changes in this PR will be included in the next version bump. This PR includes changesets to release 7 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks - I agree with this fix.
(sidenode: I think even an extremely short manual description is better than the one generated by copilot - the main thing I needed to know was that external users might want to set onabort
on signals passed to onAbortPromise
too. "addressing potential bugs and race conditions" isn't really what's going on here)
Thank you for the feedback. I'll rewrite the descriptions and skip letting AI write them. |
Co-authored-by: Simon Binder <oss@simonbinder.eu>
@simolus3 I've refactored the approach to also mitigate memory leaks, would appreciate your follow up review at your convenience |
* Note: The signal does not cancel the promise. To cancel the promise then | ||
* its logic needs to explicitly check the signal. | ||
*/ | ||
export function resolveEarlyOnAbort<T>( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks good to me, I just think we can simplify the implementation a bit because we only care about Promise<void>
:
export function resolveEarlyOnAbort(promise: Promise<void>, signal: AbortSignal): Promise<void> {
return new Promise((resolve, reject) => {
const onAbort = () => {
resolve();
};
const addAbortHandler = () => {
// Use an event listener to avoid interfering with the onabort
// property where other code may have registered a handler.
signal.addEventListener('abort', onAbort);
};
const removeAbortHandler = () => {
// Remove the abort handler to avoid memory leaks.
signal.removeEventListener('abort', onAbort);
};
if (signal.aborted) {
return onAbort();
}
addAbortHandler();
promise.finally(removeAbortHandler).then(resolve, reject);
});
}
Background
Identified a potential bug with overwriting an
AbortSignal.onabort
property.Problem
The
onabort
property is being reassigned by theonAbortPromise
function, which if a value had already been set then that logic may now not run because it's been overwritten. This can lead to unexpected behavior.Changes
addEventListener
method which allows multiple listeners to be registered.onAbortPromise
withresolveEarlyOnAbort
to centralize memory management of the abort event listener with the promise race semantics of simply resolving early on abort