Exposes underlying errors by lifting both successful and failed
results into an Either
value.
Given that errors are short-circuiting the processing of flatMap
chains, this method is useful for exposing errors such that you can
flatMap
over them.
const f: Future<number> = Future.raise(new DummyError)
// Yields a successful Left(DummyError) on completion
const fe: Future<Either<Throwable, number>> = f.attempt()
// Yields a Right(1) on completion
const fr: Future<Either<Throwable, number>> = Future.pure(1).attempt()
Delays signaling the result of this Future
by the specified duration.
It works for successful results:
const fa = Future.of(() => "Alex")
// Delays the signaling by 1 second
fa.delayResult(1000).flatMap
And for failures as well:
Future.raise(new TimeoutError()).delayResult(1000)
is the duration to wait before signaling the final result
Chains asynchronous operations.
Creates a new future by applying a function to the successful result of the source and returns the result of the function as the new future. If this future is completed with an exception then the new future will also contain this exception.
This operation is the monadic bind (e.g. Monad.flatMap
).
const fa = Future.of(() => 3)
const fb = Future.of(() => 5)
// Yields 3 + 5
fa.flatMap(a => fb.map(b => a + b))
Asynchronously processes the value in the future once the value becomes available.
WARNING: Will not be called if this future is never completed or if it is completed with a failure.
the function which will be executed if this Future
completes with a result
Given a mapping function, transforms the successful result of the source.
If the source is completed with an exception, then the new future will also be completed in an error.
This operation is the functor map (e.g. Functor.map
).
const f = Future.of(() => "The future")
const g = f.map(x => x + " is now!")
Creates a new future that will handle any matching throwable that this future might contain by assigning it a value.
This operation is the equivalent of map for handling errors. Also see transform, which can handle both successful results and failures.
const f = Future.of<number>(() => { throw new DummyError() })
f.recover(e => {
if (e instanceof DummyError) return 10
// Don't re-throw exceptions like this, use `recoverWith` instead!
throw e
})
Creates a new future that will handle any matching throwable that this future might contain by assigning it a value of another future.
This operation is the equivalent of flatMap for handling errors. Also see transformWith, which can handle both successful results and failures.
const f = Future.of<number>(() => { throw new DummyError() })
f.recoverWith(e => e instanceof DummyError
? Future.pure(10) // Fallback
: Future.raise(e) // Re-throw
)
JavaScript Thenable
implementation, needed in order to await
Future
values in async
functions.
Returns a future that mirrors the source in case the result of the source
is signaled within the required after
duration, otherwise it
fails with a TimeoutError
, cancelling the source.
const fa = Future.of(() => 1).delayResult(10000)
// Will fail with a TimeoutError
fa.timeout(1000)
is the duration to wait until it triggers the timeout error
Returns a future that mirrors the source in case the result of the source
is signaled within the required after
duration, otherwise it
triggers the execution of the given fallback
after the duration has
passed, cancelling the source.
This is literally the implementation of Future.timeout:
const fa = Future.of(() => 1).delayResult(10000)
fa.timeoutTo(1000, () => Future.raise(new TimeoutError()))
is the duration to wait until it triggers the fallback
is a thunk generating a fallback Future
to timeout to
Transforms the sources, regardless if the result is a failure or a success.
This function is a combination of map and recover, being the (type safe) alternative to JavaScript's then from the Promises/A+ specification.
Example:
import { Left, Right } from "funfix"
// Expose errors by lifting them to an Either<Error, A>
future.transform<Either<Throwable, A>>(Left, Right)
Also see transformWith.
is the function that's going to get executed in case the source signals a failure
is the function that's going to get executed in case the source signals a successful result
Given a side-effectful function that triggers an asynchronous computation,
execute it and return a Future
reference.
The given register
function will be invoked immediately to "schedule"
the asynchronous callback, where the callback is the parameter injected in
that function.
The register
function can optionally return a ICancelable
reference that can get used to cancel the running asynchronous
computation.
Example:
import { Scheduler, Future, Try, Duration, Cancelable } from "funfix"
const delay = <A>(d: Duration, f: () => A, ec: Scheduler = Scheduler.global.get()) =>
Future.create<A>(
cb => {
const task = ec.scheduleOnce(d, () => cb(Try.of(f)))
return Cancelable.of(() => {
console.warn("Delayed task was cancelled")
task.cancel()
})
},
ec
)
Note that by not returning a cancelable, the returned Future
reference
will NOT BE cancelable.
// This future is not cancelable, because we are not
// returning a cancelable reference
Future.create<number>(cb => {
setTimeout(1000, () => cb(Success(10)))
})
is the side-effectful function that will get invoked
to build our Future
, receiving a callback that's supposed to
get invoked (only once) when the asynchronous computation completes,
and that can optionally return a cancelable reference that can
get used to cancel the running computation
is an optional Scheduler reference that will get used for scheduling the actual async execution; if one isn't provided then Scheduler.global gets used, which also allows for local overrides, being a DynamicRef
Returns a Future
that will complete after the given delay
.
This can be used to do delayed execution. For example:
Future.delayedTick(1000).flatMap(_ =>
Future.of(() => console.info("Hello!"))
)
is the duration to wait before signaling the tick
is the scheduler that will actually schedule the tick's execution
Creates a race condition between multiple futures, returning the result of the first one that completes, cancelling the rest.
const failure = Future.raise(new TimeoutError()).delayResult(2000)
// Will yield 1
const fa1 = Future.of(() => 1).delayResult(1000)
Future.firstCompletedOf([fa1, failure])
// Will yield a TimeoutError
const fa2 = Future.of(() => 1).delayResult(10000)
Future.firstCompletedOf([fa2, failure])
is the list of futures for which the race is started
is the scheduler doing the needed scheduling and error reporting
a future that will complete with the result of the first future form the list to complete, the rest being cancelled
Transforms any Promise
-like data type into a Future
.
const p: Promise<number> = Promise.resolve(10)
const f: Future<number> = Future.fromPromise(p)
is the promise reference that we want to convert into a Future
is an optional Scheduler reference that will get used for scheduling the actual async execution; if one isn't provided then Scheduler.global gets used, which also allows for local overrides, being a DynamicRef
Builds an already complete Future
from a Try
value.
import { Success, Failure, Future } from "funfix"
// Already completed with 1
const f1 = Future.fromTry(Success(1))
// Already completed in error
const f2 = Future.fromTry(Failure("err"))
is the Try
value to stream in onComplete
listeners
is an optional Scheduler reference that will get used for scheduling the actual async execution; if one isn't provided then Scheduler.global gets used, which also allows for local overrides, being a DynamicRef
Maps 2 Future
values by the mapping function, returning a new
Future
reference that completes with the result of mapping that
function to the successful values of the futures, or in failure in
case either of them fails.
This is a specialized Future.sequence operation and as such on cancellation or failure all future values get cancelled.
const fa1 = Future.of(() => 1)
const fa2 = Future.of(() => 2)
// Yields Success(3)
Future.map2(fa1, fa2, (a, b) => a + b)
// Yields Failure, because the second arg is a Failure
Future.map2(fa1, Future.raise("error"),
(a, b) => a + b
)
This operation is the Applicative.map2
.
Maps 3 Future
values by the mapping function, returning a new
Future
reference that completes with the result of mapping that
function to the successful values of the futures, or in failure in
case either of them fails.
This is a specialized Future.sequence operation and as such on cancellation or failure all future values get cancelled.
const fa1 = Future.of(() => 1)
const fa2 = Future.of(() => 2)
const fa3 = Future.of(() => 3)
// Yields Success(6)
Future.map3(fa1, fa2, fa3, (a, b, c) => a + b + c)
// Yields Failure, because the second arg is a Failure
Future.map3(
fa1, fa2, Future.raise("error"),
(a, b, c) => a + b + c
)
This operation is the Applicative.map3
.
Maps 4 Future
values by the mapping function, returning a new
Future
reference that completes with the result of mapping that
function to the successful values of the futures, or in failure in
case either of them fails.
This is a specialized Future.sequence operation and as such on cancellation or failure all future values get cancelled.
const fa1 = Future.of(() => 1)
const fa2 = Future.of(() => 2)
const fa3 = Future.of(() => 3)
const fa4 = Future.of(() => 4)
// Yields Success(10)
Future.map4(fa1, fa2, fa3, fa4, (a, b, c, d) => a + b + c + d)
// Yields Failure, because the second arg is a Failure
Future.map4(
fa1, fa2, fa3, Future.raise("error"),
(a, b, c, d) => a + b + c + d
)
This operation is the Applicative.map4
.
Maps 5 Future
values by the mapping function, returning a new
Future
reference that completes with the result of mapping that
function to the successful values of the futures, or in failure in
case either of them fails.
This is a specialized Future.sequence operation and as such on cancellation or failure all future values get cancelled.
const fa1 = Future.of(() => 1)
const fa2 = Future.of(() => 2)
const fa3 = Future.of(() => 3)
const fa4 = Future.of(() => 4)
const fa5 = Future.of(() => 5)
// Yields Success(15)
Future.map5(fa1, fa2, fa3, fa4, fa5,
(a, b, c, d, e) => a + b + c + d + e
)
// Yields Failure, because the second arg is a Failure
Future.map5(
fa1, fa2, fa3, fa4, Future.raise("error"),
(a, b, c, d, e) => a + b + c + d + e
)
This operation is the Applicative.map5
.
Maps 6 Future
values by the mapping function, returning a new
Future
reference that completes with the result of mapping that
function to the successful values of the futures, or in failure in
case either of them fails.
This is a specialized Future.sequence operation and as such on cancellation or failure all future values get cancelled.
const fa1 = Future.of(() => 1)
const fa2 = Future.of(() => 2)
const fa3 = Future.of(() => 3)
const fa4 = Future.of(() => 4)
const fa5 = Future.of(() => 5)
const fa6 = Future.of(() => 6)
// Yields Success(21)
Future.map6(
fa1, fa2, fa3, fa4, fa5, fa6,
(a, b, c, d, e, f) => a + b + c + d + e + f
)
// Yields Failure, because the second arg is a Failure
Future.map6(
fa1, fa2, fa3, fa4, fa5, Future.raise("error"),
(a, b, c, d, e, f) => a + b + c + d + e + f
)
This operation is the Applicative.map6
.
Given a function that executes immediately, executes it asynchronously
and returns a Future
that will complete when the result is ready.
const sum = (x: number, y: number) =>
Future.of(() => x + y)
is the function to execute asynchronously
is an optional Scheduler reference that will get used for scheduling the actual async execution; if one isn't provided then Scheduler.global gets used, which also allows for local overrides, being a DynamicRef
Lifts a pure value into the Future
context, returning a Future
reference that's already complete with the given value.
This is the equivalent of Promise.resolve(a)
.
const f: Future<number> = Future.pure(10)
// Prints Success(10)
f.onComplete(r => console.info(r))
is the value to lift in the Future
context and that will
get signaled in onComplete
callbacks
is an optional Scheduler reference that will get used for scheduling the actual async execution; if one isn't provided then Scheduler.global gets used, which also allows for local overrides, being a DynamicRef
Lifts an error in the Future
context, returning a Future
reference
that's already failed with the given error.
This is the equivalent of Promise.reject
.
const f: Future<number> = Future.raise("Oops!")
// Prints Failure("Oops!")
f.onComplete(r => console.info(r))
is the error to lift in the Future
context and that will
get signaled as a failure in onComplete
callbacks
is an optional Scheduler reference that will get used for scheduling the actual async execution; if one isn't provided then Scheduler.global gets used, which also allows for local overrides, being a DynamicRef
Asynchronously transforms a list of futures into a future of a list.
The equivalent of Promise.all
, this is the specialized version of
Future.traverse.
Contract:
Iterable<Future<A>>
list is eagerly evaluated, transformed
from the start into an Array<Future<A>>
, so don't expect laziness in
evaluating itSample:
const f1 = Future.of(() => 1)
const f2 = Future.of(() => 2)
const f3 = Future.of(() => 3)
// Yields [1, 2, 3]
const all: Future<number[]> = Future.sequence([f1, f2, f3])
Keeps calling f
until it returns a Right
value.
Based on Phil Freeman's Stack Safety for Free.
const generate = () => {
const n = Math.random() * 1000
return n & n
}
// Keeps looping until an odd number is returned
Future.tailRecM(0, a => Future.of(() => {
return a % 2 == 0 ? Left(generate()) : Right(a)
})
is the initial seed
is the function that keeps being invoked with the previous
Left(a)
value, until a Right(b)
value is returned,
which will be the onComplete
result of the Future
reference
is an optional Scheduler reference that will get used for scheduling the actual async execution; if one isn't provided then Scheduler.global gets used, which also allows for local overrides, being a DynamicRef
Given a list of items, builds future results out of it with the specified mapping function and returns a new future that's going to be completed with the list of all generated results.
This is the generic version of Future.sequence. Useful for
processing futures in parallel, with the parallelism
factor being
configurable.
Example:
const list = [1, 2, 3, 4]
// Yields [2, 4, 6, 8]
Future.traverse(list)(a => Future.pure(a * 2))
// ... is equivalent to:
Future.sequence(list.map(_ => _ * 2))
Note that the given list
is strictly processed, so no lazy behavior
should be expected if an Iterable
is given.
But in comparison with Future.sequence, this builder has lazy
behavior in applying the given mapping function. Coupled with the
parallelism
factor, this can be used to do batched processing:
const userIDs = [1, 2, 3, 4]
// Make at most 2 requests in parallel:
Future.traverse(userIDs, 2)(fetchUserDetails)
are the values that get fed in the generator function for building a list of future results
is the maximum number of futures that are going to
be processed in parallel, defaults to Infinity
is an optional scheduler that's going to be used for scheduling the needed asynchronous boundaries
a function that takes as parameter a the generator function that's
going to map the given list
, transforming it into a list of
futures, finally returning a future that's going to complete
with the list of all asynchronously generated results
Generated using TypeDoc
Reference to the current ICancelable available for subsequent data transformations.
Protected, because it shouldn't be public API, being meant for
Future
implementations.