-
-
Notifications
You must be signed in to change notification settings - Fork 84
Comparison of Future Implementations
This is a table of the most popular Future-like libraries and the things they do and don't do. It is my intention to keep this list updated with features that other libraries implement, as well as the ones that Fluture does. If you notice a discrepancy, feel free to report it.
You may also be interested to see a more practical comparison. This exists in the form of the async problem.
Feature | Fluture | Folktale | FunTask | Ramda Fantasy |
---|---|---|---|---|
Cancellation | β | β | β | β |
Stack safety | β | β | β | β |
Continuation guarding | β | β | β | β |
Resource management | β | β | β | β |
Nice errors | β | β | β | β |
High performance | β | β | β | β |
Automatic exception catching | β | β | β | β |
Automatic caching | β | β | β | β |
Fluture allows running computations to be canceled. These computations can abort
whatever they were doing, clearing the event loop and memory. Fluture
automatically cancels computations which are no longer necessary, like the loser
of a Future.race
.
Fluture interprets the lazy computation with constant stack usage which means that no matter how big your computation might get it memory, running it will never cause it to blow the stack.
When a computation attempts to settle multiple times, Fluture ignores it automatically. A computation can only ever resolve once.
Future.hook
allows for resources to be safely disposed after use, and
it ties in with cancellation to ensure resources are also disposed when the
computation is canceled.
Fluture performs type-checking of almost all user input, in order to ensure that any mistake is quickly discovered through an informative error message:
Future.of('world').chain(thing => `Hello ${thing}!`).value(console.log)
TypeError: Future#chain expects the function it's given to return a Future.
Actual: "Hello world!" :: String
From calling: thing => `Hello ${thing}!`
With: "world"
Despite being lazy, supporting cancellation, and performing type-checking, Fluture's Futures perform roughly twice as well as native Promises (and about half as well as some to the best performing asynchronous libraries, such as Bluebird and Creed).
Fluture does not provide a way for exceptions to be automatically caught,
instead they are allowed to flow into global exception handlers such as Node's
process.on('uncaughtException')
.
Futures will re-run their computation every time they are forked, unless the
Future is cached using Future.cache
.
When you run
a Folktale Task, it gives you back a TaskExecution on which one
may call cancel
, which triggers the cancellation handlers on the original Task
to be called.
Folktale's Tasks and Futures use function composition as the basis for transformations. Every time you transform the structure, its rejection, resolution, and cancellation handlers will have higher stack usage, putting the user in potential danger to Stack Overflow Exceptions.
When a computation attempts to settle multiple times, Folktale throws an Error. This differs from Fluture, which ignores multiple rejections/resolutions. Folktale does guard against it, but moves the responsibility of stopping it from happening to its users. This can be good, because it may lead to a user catching a bug they wouldn't otherwise have noticed. Solving the bug would almost always involve implementing the same pattern though, which Fluture has included by default.
Folktale TaskExecution allows for the registration of cleanup handlers. When a Folktale Task is rejected, resolved, or canceled, it runs all the cleanup handlers.
Folktale Tasks fail with weird error messages if the user makes a mistake:
data.task.of('world').chain(thing => `Hello ${thing}!`).run()
TypeError: transformation(...).run is not a function
That said, they have plans to move to TypeScript, which should prevent some of these mistakes from ever being made.
Same approach as Fluture.
Folktale tasks are roughly three times as slow as native Promises, and roughly six times as slow as Fluture.
When a Folktale task is run
, the returned TaskExecution automatically caches
the result for you, so that multiple conversions to Folktale Future or Promise
yield the same result without having to run the computation again.
Same approach as Fluture.
Same approach as Folktale.
Same approach as Fluture.
FunTask provides no mechanisms for resource disposal.
Same approach as Folktale.
About ten times as slow as Fluture.
FunTask can catch thrown errors for you, and separates them to a third branch. This allows users to handle bugs gracefully, and separately from expected failures.
Same approach as Fluture.
Ramda Futures cannot be canceled.
Same approach as Folktale.
When a Ramda Future is resolved multiple times, its transformations and completion handlers are called multiple times as well, possibly leading to unexpected results.
Ramda Fantasy provides no mechanisms for resource disposal.
Mistakes made by users are not detected, often leading to errors like
f(...)._fork is not a function
. What's worse, because exceptions are
automatically caught it can happen that they end up in the rejection branch,
becoming indistinguishable from expected failures.
About three times as slow as Fluture.
Ramda Fantasy does catch exceptions, but it places the Error objects in the
rejection branch of the Future. This potentially causes the type of a
in
Future a b
to become a mixed type like String | Error
, leading to users
having to write weird polymorphic code. It also causes bugs to become
indistinguishable from expected failures.
Same approach as Fluture.