Considerations
Some thoughts and considerations that led to design decisions.
Promise specs
There are quite a few implementations out there. All with their strength and weaknesses and adapted to the language their written in. It's tempting to follow an existing spec like Promises/A+. I have chosen not to follow this because I think it doesn't play well with Kotlin.
For instance, the Promises/A+ spec defines then
as
promise.then(onFulfilled, onRejected)
where onFulfilled
and onRejected
are two function arguments. Both are basically bind
methods on either the
left or right side of a promise. This introduces some challenges that are hard to overcome. The biggest one is how
to handle errors thrown from the onFulfilled
or onRejected
method. The Promises/A+
states the following:
If either
onFulfilled
oronRejected
throws an exceptione
,promise2
must be rejected withe
as the reason.
This means 1 promise can actually resolve in 3 states instead of the probably expected 2, which are:
- fulfilled with value
- rejected with reason
- rejected by error from either
onFulfilled
oronRejected
I don't think this makes much sense and is not what expected when using the API. Therefor the signature for Kovenant's
then
function is:
fun <V, R> Promise<V, Exception>.then(bind: (V) -> R): Promise<R, Exception>
Any Exception
from the bind method is considered a rejection and a successful computation a fulfillment. That way
the Promise resolves in just 2 states. Kovenant's then
behaves quite similar to Option/Maybe/Try types for that matter.
So reason enough to not follow the Promises/A+ spec.
Top level functions
One important consideration is the use of top level functions versus class
or
object
bound functions. To
illustrate:
task {
foo()
}
//versus
Kovenant.task {
foo()
}
the latter has the advantage that it doesn't interfere with other frameworks that introduces such methods. But on the other hand how many frameworks bringing async functionality does a project really need? So I decided to stick with top level functions, which need to be imported explicitly anyway, to keep things simple.
sun.misc.Unsafe
I started off with an implementation that was partly written in Java and used the sun.misc.Unsafe
class. The upside
was that it was quick and very memory efficient. The downside that it requires proprietary API. So I decided that
using a generic code base outweighs the performance and memory efficiency gains. As a bonus everything is written in
pure Kotlin now.