Promise/Future: asynchrony in 'then'

Claus Reinke claus.reinke at
Thu May 2 14:28:40 PDT 2013

Thanks for the various references and explanations - it took me a while
to follow them. So, while discussion is still ongoing on the details (both 
of how to spec and what to spec), all specs seem to agree on trying to 
force asynchrony, and on doing something in 'then' to achieve this.

I suspect that at least the latter part is wrong - at least it is in conflict
with decades of design and coding experience with general monadic 
APIs, especially with the idea of providing one effect-free form of 
creation that is left and right identity to composition:

>    Promise.of(value).then(cb) = cb(value)
>    promise.then(Promise.of) = promise

My interpretation of these laws for promises is that attaching a 
callback to a resolved promise should execute that callback 
synchronously (though the callback itself may create an asynchronous 
promise, introducing its own delays).

Similarly, a callback creating a resolved promise from a future
result should not add further delays beyond those of the original
future-result-creating promise.

This does not affect design decisions about promise resolution,
so the motivating examples could still work.

One of the examples in the linked threads was roughly:

    { promise, resolve } = ...
    promise.then( r => Promise.of( console.log( r ) ) );

expecting output order 1 3 2. This would still be possible if
resolve itself was asynchronous (queuing the callbacks for the 
next or end of current turn instead of the current one) - no need 
to introduce asynchrony in 'then', it seems, not even in the implicit 
result lifting.

Explicitly providing for both synchronous and asynchronous
promises also seems more predictable and performance-tunable 
than leaving next-ticking to implementation optimization efforts.

At least some of the alternatives discussed also violate the third
law of monadic interfaces, associativity of composition:

    promise.then( cb1 ).then( cb2 ) 
    promise.then( r=>cb1( r ).then( cb2 ) )

*if* one was to add nextTicks in 'then' (which I think is a bad idea
anyway), then cb1 and cb2 should *not* be queued for the *same* 
next turn. Which would lead to the accumulation of delays that
were reported for some promise implementations, just for using
monadic callback composition.

Associativity is less of a problem for alternatives that propose
to move resolved promise callback execution to the end of the 
current turn (with or without some means to protect separate
execution threads from each other). Though that leaves the 
question of starving other queued tasks by continuing to extend 
the current turn with presumably asynchronous tasks. Again,
having explicit control over synchronous vs asynchronous
resolution of intermediate promises would help with tuning
the queuing.


More information about the es-discuss mailing list