Open novemberborn opened 11 years ago
You don't get one of the advantages of cancellation. You can't know that a promise will never be resolved after you cancel it if their optional, which means you have to look at the individual api you're calling to establish what the cancellation behaviour is and how prompt it is (current turn vs. next turn)
True, although it does solve the cancellation propagation problem by providing an official communication channel from promise to resolver without affecting the state of the promises along the chain.
Perhaps interrupts should be combined with a non-propagating .cancel()
interface.
A non-propagating cancel interface is pretty useless. I can do that myself at the moment just by extending my promise to have a .abort()
method. If we're not going to propagate the cancellation then we're not adding anything worth specifying. Also, say I have a method which supports cancellation:
function get(url) {
// return a promise that is resolved with the text body of
// the response
}
Then I want to mutate the response in some way:
function getJSON(url) {
return get(url).then(function (data) { return JSON.parse(data); });
}
If I don't care about cancellation then that's fine, if cancellation propagates that's fine, if cancellation doesn't propagate then I've lost the ability to cancel the underlying web request.
If cancellation doesn't propagate, people will still write code like that all the time, because most of the time you don't need cancellation, but then the person who does need cancellation can't use any of the nice libraries, because none of them will propagate that cancellation to the underlying asynchronous operation.
Now in terms of why we need to get cancellation propagation correct:
function get(url) {
// return a promise that is resolved with the text body of
// the response. reject if server returned status code other
// than 200
}
function getWithFallback(url, fallback) {
return get(url)
.then(undefined, function (err) {
return get(fallback); //oh dear
});
}
If cancellation propagation still rejects and we cancel a getWithFallback promise then we'll cancel the first request, but make the second request to the fall-back URL. In the best case we just waste resources, in the worst case we said "if anything goes wrong, assume the worst and self destruct, blowing up the nuclear facility so it can't fall into the wrong hands" at which point our incorrect cancellation propagation blew up a nuke. (sorry, I'm getting a little melodramatic but you get the idea).
Yes. I've written substantial amounts of code using promises for concurrency control, which also uses cancelation. I've found that my primary use cases are to communicate with the resolver, not so much canceling a promise, though because the communication channel is implemented via cancel()
that is a necessary side-effect. (See Dojo or my discontinued Promised-IO attempt.)
This experiment supports side-effect free communication with the resolver, which is rather interesting but doesn't necessarily have to be part of any spec.
In your example the onRejected
handler is needlessly naive, assuming any error necessitates the fallback. It should either test what type of error was received (is its .name
OperationCancelled
?), or if that's not acceptable we need a different handler for when the promise transitions to a canceled
state. Anyway, let's discuss such subtleties in #2.
You're missing the point, consider the following:
function getPromised(url) {
return get(url)
.then(undefined, function (err) {
log(err);
throw err;
});
}
It shouldn't need to do anything special to determine what type of error was thrown. It shouldn't need to do anything special to deal with cancellation because it doesn't do anything with cancellation. Cancellation should only need to be considered by the PromiseLibraries/Implementations, the person actually initiating/using cancellation and the person managing the underlying IO operation.
+1 to @ForbesLindesay's point; you cannot have such trivial transformations be subject to dealing with an extension like cancellation.
Quotes come from #1.
I find this communication channel super useful, not just to signal "cancel me" but to actually send a message to the resolver, e.g. "the inbound request socket was closed".
I've been prototyping interrupts in Legendary, see the examples in https://github.com/novemberborn/legendary/tree/interrupts. This lets you send messages to a resolver which can then decide to ignore the interrupt, or fulfill or reject the promise. It lets you implement cancelation tokens, for instance.
Thoughts?