Fluture
Fluture copied to clipboard
Parallel transformations are run twice when one of the Futures resolves the other synchronously
First discovered in https://github.com/fluture-js/fluture-hooks/pull/6, this is one of several thinkable issues that arise once control over the resolution of a Future is taken outside of the Future constructor, for example:
let exportedRes;
// We're defeating Fluture's design and exfiltrating the res function:
const m1 = Future ((rej, res) => {
exportedRes = res;
});
// Then we create another Future which forces the resolution of the former:
const m2 = Future ((rej, res) => {
res (42);
exportedRes (42);
});
// Now when we (re)combine these two Futures with Fluture, Fluture
// is going to run the effect of `both` (or whatever combinator was
// used) twice: once as a result of m2 starting a parallel process,
// and once more for m1 resolving as a result of that parallel process.
both (m1) (m2)
The example above is very similar to what fluture-hooks
does to achieve its parallel applicative instance. Arguably, this is not a bug, because the user is abusing Fluture: they're manually combining m1
and m2
outside of Fluture's API for combining Futures. However, since it's possible to achieve this at all; I still want to record it as a bug and think about how Fluture should behave under these scenarios.
It's might also be worth addressing why a user would want to exfiltrate the res
function in the first place, and work towards allowing the user to substitute it with a more idiomatic pattern.
In the case of fluture-hooks
, it is required because we need a mechanism for two Futures to hang until some external process has finished its thing, at which point both Futures should resolve.
Will this issue be resolved, if rej and res functions are wrapped in a way that makes them idempotent?
Hi @safareli :)
No. Rej and res are already wrapped for idempotence:
https://github.com/fluture-js/Fluture/blob/f349eb4555871484828d796d9c00d63aff7209f9/src/future.js#L153-L169
The problem happens because the internal mechanics of both
(or any parallel combinator), and how it combines the two parallel Futures.
It's built on the assumption that the userland of one Future doesn't trigger signals in the other. So the signals from the other future are not guarded while running the one Future.