Promise/Future: asynchrony in 'then'
claus.reinke at talk21.com
Sat May 4 08:04:46 PDT 2013
>> That part I wouldn't be so sure about: in all monads, the .of equivalent
>> is effect-free (in an IO monad, it does no IO; in a non-determinism
>> monad, it is deterministic; in a failure/exception monad, it does not
>> fail; in a count-steps monad, it doesn't count).
>> If you look at those identity laws at the top again, you'll see that
>> Promise.of cannot introduce a delay for these laws to work out
>> (otherwise, the left- and right-hand sides would have different
>> numbers of ticks/turns).
> As I said, the number of ticks is unobservable if you're writing
> effect-free code.
In my use of the the term above, the "effect" of the Promise monad
would be to provide a value "maybe now, maybe later", and an
"effect-free" '.of' would be an already resolved Promise ("value
Adding ticks in operations that should allow for "effect-free" passing
of intermediate results is observable in slow-downs. That was the
topic of the blog post that got me to look into this in the first place
(performance issues with promise implementations, see thread
The point of insisting that promises implement a monadic interface
is that promises can reuse abstractions built for monads - that also
means that passing intermediate values around should not cause
additional delays. For instance, in the 'liftA2' example from one of
the issue tracker threads:
there are several occurrences of '.then', via 'map' and 'ap', that
should not delay the result by several additional turns - the only
asynchrony in using 'liftA2' over promises should come from the
promise parameters and possibly from the callback parameter.
However, you seem to be referring to side-effects instead (effects
beyond returning a value in an expression, beyond the specified
effect of a given monad).
Side-effect-free code is difficult to write in JS - I would be surprised
if most promise implementations were not full of side-effects
(internal queues, shared pipelines, resolution). Also, so many
examples of using promises involve side-effects that this seems
to count as an established practice.
Which means that the additional code queuing will also be
observable in code reorderings, not just delays. Which is, indeed,
the rationale for attempting to add delays in a normalized fashion,
as you state below:
> If you're not writing effect-free code, then as I said before, keeping
> the number of ticks the same regardless of the state of the promise
> when you call .then() on it is important for consistency, so it's easy
> to reason about how your code will run.
Given that many JS APIs still are heavily side-effect biased, we'll
need to take that into account. And in that world, adding delays in
parts of the promise API that should implement the common
monadic interface is very much observable, and will cause code
written against this common interface to behave differently when
run over a promise than when run over another monad.
More information about the es-discuss