Skip to content
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

Interrupts #3

Open
novemberborn opened this issue Dec 26, 2012 · 7 comments
Open

Interrupts #3

novemberborn opened this issue Dec 26, 2012 · 7 comments

Comments

@novemberborn
Copy link

Quotes come from #1.

[@domenic] The main problem with cancellation that kriskowal and I have considered is that cancelling provides a communication channel going from the promise to its resolver, representing a capability leak. Before, you could hand out promises to mutually-suspicious parties. With a cancelable promise, each party can affect the other party's operation, which is bad.

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".

[@skaegi] I sort of agree with the idea that canceling is like exception handling with similar unwinding the stack considerations for promise chains, but I think one difference is that the user initiating cancel is outside the stack. I'm not sure what the synchronous analog for that is? Thread.interrupt?
[snip]
It might be better if the underlying Deferred could optionally choose to react to the cancel request but that might just be an implementation detail.

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?

@ForbesLindesay
Copy link
Member

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)

@novemberborn
Copy link
Author

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.

@ForbesLindesay
Copy link
Member

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.

@ForbesLindesay
Copy link
Member

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).

@novemberborn
Copy link
Author

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.

@ForbesLindesay
Copy link
Member

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.

@domenic
Copy link
Member

domenic commented Dec 29, 2012

+1 to @ForbesLindesay's point; you cannot have such trivial transformations be subject to dealing with an extension like cancellation.

@ForbesLindesay ForbesLindesay mentioned this issue Jan 2, 2013
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants