-
-
Notifications
You must be signed in to change notification settings - Fork 46
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
Unexpected try/catch/finally semantics #18
Comments
One thing to note: I can't do anything about how Moreover, the point of I'll come back to consider separately your other question of if the error thrown into the canceled function should be detectable in a special way. But just wanted to point out that your concerns around Your final code snippet there seems, to me, an unusual attempt to work-around not liking how Perhaps it's a good idea to take a step back and contemplate why you're actually concerned about how |
Sorry I gave you the wrong impression, I've been coding for well over 20 years, I'm definitely NOT trying to work around finally blocks, or to avoid the use of them :)
My problem is that code path 3 is completely hidden and yet still triggering the finally block. The only way to catch the I don't want to change the current behaviour though, so that's why I'm suggesting a secondary |
For reference, the line of code that resumes the generator upon cancellation, so that any waiting Line 55 in 3f33c5c
The |
But to a larger point... I'm still confused by the semantic that you want the CAF-wrapped function to be resumable after it's been canceled? That doesn't really make any sense to me. By throwing in an exception, rather than completing the generator with Allowing a Let me ask it this way: What logic do you want to do differently in the case of cancellation? Are you basically trying to make CAF-wrapped functions which can resume even if they've been canceled? Or are you trying to do different cleanup logic in the case of cancellation vs normal completion? |
Here's a pseudo example of the general pattern I'm after:
Ideally I would've liked this where I have more control to do all the cleanup in the catch because you can have full contextual information on why things were aborted or errored all in the same place:
So ya I'm actually trying to do both resumable cancelled functions and abort specific cleanup logic. I can see a good argument against since as you said it means the actor here could capture the abortReason and not actually abort at all, but I wonder if that's ok because it's an opt-in to the new mechanics because it uses |
Maybe the reason I'm finding this different usecase is specially because I'm treating it as an Actor pattern and trying to have it cleanup itself instead of having an external entity try to manage state. In which case maybe the term and meaning of 'abortWithThrow' is the problem since the actor could decide not to abort. So what about calling it |
OK, that context helps, thanks. I still am stuck on the principle that But if I granted that being able to determine the "reason" for reaching I recognize that you may want some information (like the What I'm envisioning is something like this: CAF(function *main(..){
try {
// ..
}
catch (err) {
// ..
}
finally {
if (CAF.isCancelled()) {
let reason = CAF.cancellationReason();
// ..special cancellation cleanup logic
}
// ..
}
}); The If we did something like that, would it be sufficient? This is a rather narrow use-case for the generality of CAF to tackle, so I'm trying to thread the needle in terms of doing so in the most conceptually pure and limited fashion possible. |
An alternate approach without the magic methods could be to add an CAF(
function *main() {
try {
// ..
return "OK";
}
catch (err) {
// `return` here stuffs the `err` into the `retVal` parameter of the `onFinally` handler (if any)
return err;
}
finally {
// this will always still run, first
return 42;
}
},
function onFinally(reason,retVal){
if (reason) {
// handle cancellation cleanup
retVal; // 42
}
else if (retVal instanceof Error) {
// handle exception/rejection cleanup
reason; // undefined
}
else {
// handle normal completion cleanup
retVal; // "OK"
reason; // undefined
}
}
); |
I think your view on Either way, I've played with a couple of dead simple solutions for a couple hours now (good test cases take forever!) and I definitely agree that injecting the Thanks anyways, have a good one. |
From this twitter thread, the idea arose to check CAF(function *main(..){
try {
// ..
}
catch (err) {
// ..
}
finally {
if (signal.aborted) {
// ..special cancellation cleanup logic
}
// ..
}
}); Hadn't occurred to me that I'm sorry you felt I was being too narrow in the exploration. My default is to try to find the minimal/narrow solution to any problem, rather than throwing a large solution at a small problem. In any case, thanks for filing the issue and for the discussion. I certainly will consider in future evolution of CAF. |
Hmm that's interesting, and rather simple enough to check. I'll give that a try. And I wouldn't characterize your solutions/suggestions as being too narrow. You've given a number of other solutions, so much appreciated, thx! |
After my first run at creating a couple of CAF runnables and playing with cancellation I've realized adding a finally changes the semantics in an unexpected way (to be fair this is documented but I didn't quite realize the implications until I ran into it).
Example running the following should log 'try'
Meanwhile add the finally block and now this will print 'try', 'finally' (Note it does not print 'after')
This was not quite what I was expecting, and now finally blocks get triggered from 3 paths instead of the normal 2 (3rd path is the hidden cancel signal). I was actually expecting the abort to throw a special Cancel Error or even just the value passed into
.abort('hello abort')
and then be able to catch it and check for the special cancel error or a cancel Symbol. Obviously if you don't have a try/catch around your yields then you're not going to catch any errors and the whole runner is finished.I can understand the behaviour makes sense since you're not really wrapping the signal promise in the try catch, but it seems weird that it acts like a hidden error and triggers the finally only. So I'm wondering if there would be value in adding a new
abortWithThrow(reason)
that calls the generator's throw with some kind of cancel error/symbol as part of aborting the AbortSignal.Then this would print 'try', 'cancelled: hello abort', 'after'
The text was updated successfully, but these errors were encountered: