-
Notifications
You must be signed in to change notification settings - Fork 29
Avoiding the third state #14
Comments
In the course of working on #12, I've hit upon a variant on this proposal that I think is observably equivalent, but conceptually very different. If we also nail down the guard syntax to just a special-case Create a global function named One big difference between this magical-object proposal and the original proposal is that given try {
f();
} catch cancel (e) {
throw e;
} in the original proposal the rethrow will create a As I said, this is conceptually very different; the OP of this issue says that it's still a |
If you require that cancellations never hit |
I question this premise. Why should
You're saying Semantically, catches are move-on-points in code, where code defies failure. I.e. "We don't care about no flower, we're drawing a chair anyway." is the semantic meaning of the code above. Having cancellation defy this, seems confusing. What's its scope then? And what if |
The premise is explained in detail in https://github.com/domenic/cancelable-promise/blob/master/Third%20State.md and is core to this proposal. The answers to the rest of your questions can be found from the spec. Note that |
I read three times. Liberty taken: function myCancelToken() {
let defer, token = new cancelToken(cancel => defer = cancel);
token.cancel = defer;
return token;
} I think your premise is flawed, sorry. Had I wanted chair to be cancelled I could have passed My understanding is that people want to cancel _a_ fetch, not all of them. Semantically, what follows a catch does not rely on completion of what preceded it. I see this: "Once an operation is canceled, any fulfillment or rejection reactions to it are no longer valid." But not the converse: "Once an operation is fulfilled or rejected, any cancellation reactions to it are no longer valid." So I wonder if cancellation happened during fetching of |
@jan-ivar You end up having to do I'm definitely against any proposal that does this. On the other hand - I'm pretty fine with just running |
@benjamingr To me, cancellation at an individual method API level, is very different than cancelling something with its own event loop. |
I think we're mixing things. Cancellation of something with its own event loop is different, you're cancelling another thread basically. That thread is often doing multiple independent jobs, each of which is fully caught and logged. I agree that in order to tear something like that down, you need a cancellation path that bypasses local catch handlers, to avoid spew. But that's a higher-order cancel in my book. If we want that, we should consider an API like: cancellableChain(token)
.then(() => foo())
.then(() => fetch('a').catch(e => {})
.then(() => fetch('c').catch(e => {}); not overload the first cancellable operation, whatever it is, as that's weird and confusing: foo()
.then(() => fetch('a', {}, token).catch(e => {}) // note: token cancels whole chain!
.then(() => fetch('c').catch(e => {}); In contrast, cancelling an individual |
...because such cancellations are in the domain of the worker, if you will, not outside of it. |
@jan-ivar it's not, the problem is stuff surrounding it might treat exceptions as well... exceptions. Cancellation as exceptions failed because of exactly that - the code "recovering" catches the exceptions and thinks it's a recovery - you would have to special-case every single exception handling or reporting mechanism to test against that.
Has to become:
|
@benjamingr My point in #14 (comment) is that there's typically no "stuff surrounding it" in the simple case of cancelling an asynchronous fetch or a single promise chain, code usually written by one author. This "outside party wants to cancel" higher-order thing is a different beast IMHO than what people are asking for, which I think is better covered by a simple pattern that can be adopted to not just stop waiting for things, but actively rescind activity. |
It does seem there is often conflation between cancellation as "registering disinterest, which may lead towards unnecessary work no longer being performed" and cancellation as "a guarantee that any ongoing or future task will not proceed, even if other things are interested in the result". In the general case, it might be helpful to clarify which is being discussed in each comment. |
Would something similar (or maybe opposite) to Java's interrupted bit help in this situation? I agree with @jan-ivar that it seems very dangerous to treat cancellations as very different from normal exceptions, but maybe a happy medium would be that if you catch a Cancellation but don't explicitly "un-cancel" it, then any future async operation will immediately re-throw the cancellation (it would probably need to be reset once the entire stack has unrolled). A disadvantage here is that it's clear in Java when this is necessary, since InterruptedException is a checked exception, so it must be declared and explicitly caught. But I'm very wary of the situation where code that expected it would catch everything is now no longer catching everything. |
Cancellations are not normal exceptions, they are not exceptional cases, they are different. If I cancel an HTTP request because a user typed another letter in a typeahead there is nothing exceptional around that and modeling that through exceptions is strange. In fact in most cases I use cancellation in nothing exceptional has happened.
I'm fine with any solution where |
More or less implemented the OP's approach now; closing. Unfortunately |
Some people have brought up that introducing a third main type of completion is a big deal and that maybe it should be avoided.
I think we need to preserve the following requirements no matter the shape of the solution:
catch (e) { ... }
does not catch cancelationsOne proposal that still meets all these requirements is as follows:
catch (e) { ... }
catch cancel (e) { ... }
?throw new Cancelation("...")
. I'd even be in favor of introducing a global function namedcancel
so you can dothrow cancel("...")
, but people might object to introducing a new global with such a generic name. (We got away with it for fetch and close and open and others, though.)I think this alternate proposal is not as good as the third state proposal for a couple weak reasons:
As such I plan to continue pursuing the third state approach. But I wanted to leave this as a record of my thoughts on the matter. If someone wants to take ownership of an alternate proposal I'd be willing to work with them.
The text was updated successfully, but these errors were encountered: