slightlyoff / Promises

DOM Promises IDL/polyfill
Apache License 2.0
155 stars 28 forks source link

Need a way to synchronously resolve a Future #47

Open sicking opened 11 years ago

sicking commented 11 years ago

For background and details, see http://lists.w3.org/Archives/Public/www-dom/2013JanMar/0202.html http://lists.w3.org/Archives/Public/www-dom/2013JanMar/0205.html

In short, in some cases we need the ability to resolve a Future synchronously. In particular, for some APIs it's important that the callback which indicates success/failure happens at a particular instance.

For Event dispatch, it's important that the callback is called during the actual event dispatch. Otherwise doing things like calling Event.preventDefault() or Event.stopPropagation() will have no effect.

There will likely be other similar scenarios. I could definitely imagine that we'll run into situations when we'd want to do something like:

function topLevelCallback() {
  setState1();
  resolver1.accept(...);
  setState2();
  resolver2.accept(...);
  setState3();
}

where topLevelCallback is called as a separate microtask and where we want the callbacks which are triggered by resolver 1 to see "state 1" and the callbacks triggered by resolver 2 to see "state 2".

Ideally the cases when we need to synchronously resolve a Future will be rare and will only happen when we're basically at the top of the callstack. Synchronously dispatching Events is an obvious exception and really is a wart, but one that we're many years too late to fix.

domenic commented 11 years ago

I think this is a very strong indication that you are trying to use DOMFutures somewhere where they don't belong. Promises represent asynchronous operations; to represent a synchronous operation, you should use a function call.

sicking commented 11 years ago

So you think we should not have a EventTarget.once function then? I certainly believe that that's a valid answer, it just would have been cool if we could make that work. Note that most events are asynchronous, just not all of them.

Firing the .then-callback at a very specific time certainly goes against the idea that you can register .then-callbacks much after a promise has been resolved. So I can definitely see that as an argument that this issue should be rejected. But it does limit the types of asynchronous patterns that we can solve with Futures.

domenic commented 11 years ago

I think EventTarget.once should exist but not return a DOMFuture (i.e. it should act the same as Backbone or Node's once or jQuery's one).

Firing the .then-callback at a very specific time certainly goes against the idea that you can register .then-callbacks much after a promise has been resolved.

I didn't even realize this aspect of your proposal. The idea would be that attaching a handler with then after the event has fired does nothing? E.g.

let future = myElement.once("focus");

future.then(() => console.log("1"));

myElement.focus(); // synchronously logs "1", eek.

future.then(() => console.log("2")); // but "2" is never logged!?!?

If so, this breaks some of the most fundamental rules of promises, and further indicates they're the wrong abstraction here.

domenic commented 11 years ago

On further thought, this seems somewhat similar to #37, where you're trying to use promises to model streams. Events are definitely streams, not promises. (See functional reactive programing.)

The only confounding factor is that there are some events that really should be promises, like DOMContentLoaded. (After all, if DOMContentLoaded has already fired, you still want your handler to go, as shown by jQuery's "ready" behavior.)

sicking commented 11 years ago

No, the console.log("2") would still happen. It would just happen when the world is in a different state. All I'm saying is that my proposal tries to guarantee that an early-registered callback would see a specific state of the world. Late-registered callbacks would see a different state.

I also don't understand what you are saying about proposal #37. It only uses Futures to model a single value from the stream, as well as a (separate) Future for modeling the end of a stream. But it seems like this discussion belongs over in #37.

sicking commented 11 years ago

I think the higher meta-question here is: Are Futures appropriate to use in situations when we want to asynchronously deliver a success/error signal, but where it's critical that the signal is delivered while the surrounding world is in a particular state.

As things stand right now, Events are a good fit in situations when you have callbacks that fire zero to multiple times, particularly in response to UI actions.

Futures are a good fit in situations when you have a result which is delivered asynchronously, and that result is either a value or an error.

The question in this issue is: Are Futures a good fit when you have a result which is delivered asynchronously, and it's important that the world is in a particular state when the value is delivered.

I think it's ok to say "no". Trying to ensure that the world is in a particular state, especially through chained Futures, is non-trivial. But it does potentially lose us a lot of use cases were we can't use Futures, and will have to use plain callbacks or Events.

slightlyoff commented 11 years ago

I'm trying to understand what guarantees about the world's state are required, and why they aren't being packed into the value passed to the future instead of being left in the environment?

sicking commented 11 years ago

See http://lists.w3.org/Archives/Public/public-webapps/2013AprJun/0217.html

juandopazo commented 11 years ago

DOMFuture used to have a way to resolve synchronously. It made a lot of sense for cases in which we know that at resolution time we're in another tick, like in after a network request, to avoid an unnecessary performance hit. Does anyone know why it was removed?