-
Notifications
You must be signed in to change notification settings - Fork 30.1k
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
Add a utility for knowing an AbortSignal was aborted #37220
Comments
(I am happy to implement - though I haven't landing the weak event handlers PR yet because I am very hesitant about it) |
The common usage pattern I anticipate for this util is: // ..
await Promise.race([
promise1,
promise2,
aborted(signal)
]); FWIW, in CAF, the promise I create for an abort signal actually rejects when the token is aborted... this makes detection of the abort a little easier (like with In any case, I would envision shipping some sort of polyfill for this util along with CAF, because CAF is designed to work in both Node and the browser. So interop with the browser env is important here. Maybe it's a good idea to float such a util with WHATWG (or whoever) to see what their interest level is? |
@getify I think we'll use it a ton in core as well, we currently have a ton of code that looks like: if (signal.aborted) {
// cleanup
} else {
const listener = (err) => {
// cleanup
};
signal.addEventListener('abort', listener);
resource.on('done', () => signal.removeEventListener('abort', listener);
} That would become: aborted(signal, resource).then(() => /* cleanup */); Other than being significantly shorter the selling point for me here is that the code is significantly safer as well - since you don't have to remember to remove the listener to the signal.
Sure, I'll open an issue. |
I'm also wondering if something like what was done with Could This approach would make it significantly easier for me to migrate CAF to using this util without introducing a major breaking change. |
@getify thanks, opened whatwg/dom#946 I think we can ship I feel strongly that if we want to pursue a standards contribution rather than a Node.js utility we should not ship a experimental utility in the meantime but I am very interested what Jake/Domenic/Anne have to say.
I think it's worth exploring. I think one of the cool bits is that if it's a utility and we don't actually have to call Can you write a quick code example of what you mean here so I make sure we're on the same page? |
I was sorta envisioning logic like this: async function aborted(signal) {
if (signal[Symbol.abortPromise]) {
return signal[Symbol.abortPromise];
}
// ..
} In CAF, then, I could continue to create the promise myself, and instead of (or in addition to) attaching it as a Then, CAF-using code would just look like: Promise.race([
// whatever
aborted(signal)
]) ...instead of needing the special casing like: Promise.race([
// whatever
signal.pr || aborted(signal)
]) The promise I vend in CAF could reject instead of fulfill, so that CAF's logic and use-cases are still preserved. But then Node could create a fulfilling-promise by default, if that's more desired. BTW, the reason CAF needs to vend its own promise is not just that reject semantic... but also because CAF relies on being able to pass along a specific reason to that promise rejection, so that it's easy to detect in |
Definitely think some utility code would be helpful. I'd actually rather not optimize it for Let's back up and review the requirements for a bit... what exactly is needed...
Given that, a wrapper such as the following would work... (haven't tested and in a hurry typing this so may be errors but you should get the idea) async function cancelable(asyncFn, cancelFn, signal) {
return funcction (...args) {
if (signal.aborted)
throw new AbortError();
signal.addEventListener('abort', cancelFn, { once: true };
try {
return await Reflect.call(this, args);
} finally {
signal.removeEventListener('abort', cancelFn);
}
}
}
// ....
const ac = new AbortController();
async function foo() {}
async function cancelFoo() {}
const fn = cancelable(foo, cancelFoo, ac.signal);
await fn(1, 2, 3); As I said, this is rough but something like this should work to address the common pattern. |
If it's not exposed as a promise, it won't be of any use to CAF (or other libs of the same vein), which has a few years head start on this effort here in terms of how it's modeled async cancellation via a promise attached to the abort signal, and the heavy use of |
Is there still interest in this? I would be interest in giving this a try
Also same question |
Feel free to pick this up! |
Fixes: #37220 Refs: #36607 PR-URL: #46494 Reviewed-By: James M Snell <[email protected]> Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: Benjamin Gruenbaum <[email protected]> Reviewed-By: Antoine du Hamel <[email protected]>
Is there any part of the |
I think @benjamingr did float the idea of standardization? |
I don't have time to engage in the capacity required for the standardization effort and my employer won't sponsor that work. I am happy to help with such an effort though. Personally, I am in favor of standardization. |
What would be required (have no idea about the processes involved 😅) would love to help |
Basically engage in the whatwg issue :) Join the chat whatwg has some nice folks |
Fixes: #37220 Refs: #36607 PR-URL: #46494 Reviewed-By: James M Snell <[email protected]> Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: Benjamin Gruenbaum <[email protected]> Reviewed-By: Antoine du Hamel <[email protected]>
Fixes: #37220 Refs: #36607 PR-URL: #46494 Reviewed-By: James M Snell <[email protected]> Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: Benjamin Gruenbaum <[email protected]> Reviewed-By: Antoine du Hamel <[email protected]>
Hey,
Speaking to library authors in the ecosystem it appears that this pattern of code is very common (also in our code):
It would be very useful to be able to write this code in a more ergonomic way, talking to @getify about this in the CAF repo a utility was suggested:
Which would let you do
Any opinions on this? (Personally I am in favour) If we add such an API under what module would it live?
cc @jasnell @addaleax ?
The text was updated successfully, but these errors were encountered: