Configurable timing / next-turn function for sync resolutions #135

Open
unscriptable opened this Issue Mar 29, 2013 · 12 comments

Projects

None yet

7 participants

@unscriptable
Member

Just opening this ticket to resurrect the talks about a configurable next-turn feature in when.js. There are some edge cases where synchronous resolution is needed.

@scothis mentioned window.unload, recently.

@Mossop also expressed the need to enforce sync resolutions for "for API stability and consistency reasons" on this Promises/A+ thread.

@briancavalier suggested something like this:

var mySyncWhen = when.configure(invokeSynchronously);

This creates a single instance of when that uses invokeSynchronously as a timing / next-turn function.

Thoughts?

@briancavalier
Member

Yeah, having fine grained control over the handler scheduling could be useful in some scenarios. I'd definitely like to learn more about @Mossop's situation, and see if when.js can help.

There are definitely some gotchas to be aware of. For example, providing a synchronous scheduler, like my example above will give you a promise implementation and when (named mySyncWhen) function that allow synchronous resolutions, but cannot guarantee them. There are still many cases where the resolution must be async, for example, if there are other promise implementations in play, consuming them with mySyncWhen cannot force them to be synchronous. That includes any promises created by another async-configured when.js.

It also only allows sync resolutions when the return values of handlers are either not promises, or are already-fulfilled/rejected promises.

I'm not saying we shouldn't do it :), but I wanted to highlight some of the things we'll need to be aware of with allowing a configurable scheduler.

There are other uses for a configurable scheduler, for sure. For example, if a new JS platform emerges with some new scheduling API (recent example: vert.x), it makes it easier for when.js to adapt to that platform without us having to add a new sniff and do a new release.

@thanpolas
Contributor

+1, sorry for missing this one.

@alanclarke

+1 this is a very convenient feature for unit tests

@briancavalier
Member

@alanclarke One problem to be aware of in using a synchronous scheduler for unit tests (and then using an asynchronous scheduler in production) is that assumptions about synchronicity in your application code might pass your unit tests but fail when used in fully async mode in production.

@stefanpenner

seems deviating between testing and prod is going to turn into nightmarish production bugs.

@briancavalier
Member

@stefanpenner Completely agree. I definitely would advocate using the exact same scheduler in dev, testing, and production. It's also possible to create a scheduler that violates other Promises/A+ guarantees like callback ordering.

I think we would have to say that, in general, using your own scheduler voids your when.js warranty :)

@briancavalier
Member

And we may be less inclined to spend effort investigating issues that are filed where a custom scheduler is in play.

@alanclarke

agree, we need to be aware of this issue. Assuming we are, it was very cool to be able to use when.js synchronously with fake timers e.g. http://sinonjs.org/docs/#clock to ensure things happened when they should.

@briancavalier
Member

@alanclarke et. al. The when3-proto branch now allows you to create your own when.js core and supply it with a scheduler. Here's how when.js creates it's own core for backward compatibility, which uses the same scheduler as version 2.x. We've tested it against a synchronous scheduler and it works just fine. So, while it's unlikely we'll actually provide a synchronous scheduler in the when.js package, you will be able to instantiate a version of when that uses one.

@alanclarke

@briancavalier, awesome sauce 👍

@medikoo medikoo referenced this issue in promises-aplus/promises-spec Aug 24, 2013
Closed

Specifying (or not) sync/async resolution behavior #4

@bsnote
bsnote commented Oct 2, 2013

If it were possible to determine whether code is executed at the same tick, then we could just check that and invoke resolution at the next tick, otherwise invoke it at the same tick. The same can be done by adding a new method to deferred object which designates the return point of the promise. For example:

var when = require('when');

execute().
  then(function() {
    console.log('B');
  });

console.log('A');

function execute() {
  var deferred = when.defer();

  process.nextTick(function() {
    // if deferred.end() was called then it resolves immediately, otherwise it resolves at the next tick.
    deferred.resolve();

    console.log('C');
  });

  deferred.end();
  return deferred.promise;
}

Output (with deferred.end() call):
A
B
C

Output (without deferred.end() call):
A
C
B

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment