Creating Tasks
The following functions are used to create Tasks. They can be used from either the Task
class (for example Task.of
) or imported directly from the library as a function (simply, of
).
Many of these methods also exist on the Task instance. They exist here in their pure form in the hopes that the proposed Pipeline operator ever makes its way into the language.
of
Creates a task that will always succeed with the given value. Also aliased as succeed
. Similar in use to Promise.resolve
.
empty
Creates a task that will always succeed a result of void (undefined). Useful for chaining tasks which create side-effects, but don't return a useful value.
succeedIn
Creates a task that will always succeed with the given value, but will take some number of milliseconds before it does. Useful for simulating latency or inserting timeouts in a task chain.
succeedBy
Creates a task that will always succeed with the result of a function. Useful for bringing global state or side-effects into a task chain.
fail
Creates a task that will always fail with the given error. Similar in use to Promise.reject
.
failIn
Creates a task that will always fail with the given error, but will take some number of milliseconds before it does. Useful for simulating latency or inserting timeouts in a task chain.
all
Creates a task that will always run an array of tasks in parallel. The result in a new task which returns the successful results as an array, in the same order as the tasks were given. If any task fails, the resulting task will fail with that error.
All task error and value types must be the same. If you need different types for the items of the array, consider using ap
instead.
Works similarly to Promise.all
.
allSuccesses
Creates a task that will always run an array of tasks in parallel. The result in a new task which returns the successful results as an array, in the same order as the tasks were given. Failed tasks will be excluded.
This can never fail, only return an empty array. Unliked Task.all
, the resulting array is in order of success, not initial input order.
sequence
Creates a task that will always run an array of tasks serially. The result in a new task which returns the successful results as an array, in the same order as the tasks were given. If any task fails, the resulting task will fail with that error.
All task error and value types must be the same. If you need different types for the items of the array, consider simply chaining the tasks together in order.
Additionally, a second parameter can be added to allow concurrency. The default concurreny is 1
task at a time, but may be increased. Setting this concurrency to Infinity is the same a Task.all
.
firstSuccess
Creates a task that will always run an array of tasks in parallel and return the value of the first successful task. If all tasks fail, the error result will be an array of the error results from each task in the same order as they were given.
never
Creates a task that will never succeed or fail. Uses the TypeScript never
type which allows the type checker to detect impossible cases and warn the developer at compile time.
fromPromise
Converts a Promise into a Task. Because Promise start in a "pending" state, that process will already be running before the Task is forked. This means that if two tasks chain off this one, the initial Promise (say, a web request) will only happen once. Consider using fromLazyPromise
instead to bring Promise more in line with the lazy Task philosophy.
Promise's do not track an error type (one of the reasons Tasks are more powerful) so the resulting Task is unable to infer the error type as well. It is recommended to pass it in as a generic.
fromPromises
Converts an array of Promises into an array of Tasks and joins them with Task.all
Promise's do not track an error type (one of the reasons Tasks are more powerful) so the resulting Task is unable to infer the error type as well. It is recommended to pass it in as a generic.
fromLazyPromise
Given a function which returns a Promise, turn that into a Task. This allows the Promise not to start until the Task forks (following the lazy philosophy of the rest of the library). This also means if two tasks chain from this one, the promise creating function will be called twice. See onlyOnce
if you wish to avoid this.
Promise's do not track an error type (one of the reasons Tasks are more powerful) so the resulting Task is unable to infer the error type as well. It is recommended to pass it in as a generic.
wrapPromiseCreator
Given a function which returns a Promise, create a new function which given the same arguments will now return a Task instead.
race
Creates a task that will always run an array of tasks in parallel. The first task to finish is the resulting error or value. Useful for implementing network request timeouts by racing a task which fails in x milliseconds and a task which makes the request.
Works similarly to Promise.race
.
external
Creates a Task which can be controlled externally by providing reject
and resolve
methods. Must provide error and value types as generics.
Useful for integrating with callback based libraries and APIs.
emitter
Creates a Task that wraps a callback. Also returns an emit
callback which when called, executes the callback. If the callback throws an exception, that is the error response of the task. Otherwise the returned value of the callback is considered a success.
Must provide error and value types as generics.
Useful for integrating with callback based libraries and APIs. Provides automatic exception handling.
map2
Given two tasks, run the successful values through a mapping function to combine them into a new output. Unlike Task.all
, this allows each task to be of a different type.
The function must be curried. That is, each parameter is handled one at a time. A version of this function which is not curried is available as zipWith
.
map3
Given three tasks, run the successful values through a mapping function to combine them into a new output. Unlike Task.all
, this allows each task to be of a different type.
The function must be curried. That is, each parameter is handled one at a time.
map4
Given four tasks, run the successful values through a mapping function to combine them into a new output. Unlike Task.all
, this allows each task to be of a different type.
The function must be curried. That is, each parameter is handled one at a time.
If you need to operate on more than 4 tasks, consider using ap
which can combine an arbitrary number of tasks using a mapping function.
loop
Allows the construction of a recursive, asynchrous loop. Given an initial starting value, call the currrent loop function and return a Task that contains either a LoopBreak
or LoopContinue
instance. The instance holds on to the current value. Will loop until it encounters a LoopBreak
.
This is a simplified version of what some will accomplish with a Array.prototype.reduce
which uses the current Promise as it's value.
reduce
Works exactly like Array.prototype.reduce
, but asynchronously. The return value of each reducer must return a Task.
zip
Given two tasks, return a new Task which succeeds with a 2-tuple of their successful results.
zipWith
Given two tasks, return a new Task which succeeds by running the successful results through a mapping function. Very similar to map2
, but zipWith
uses 1 parameter per successful Task. map2
uses a curried mapping function.
flatten
Given a task which succeeds with another task, flatten into a single task which succeeds with the result of that nested task. Often this can be avoided by using chain
.
ap
The applicative. If you know what that means, you'll be excited. If not, it is fine. This is a low level tool that helps build more complex features.
ap
starts with a Task which succeeds containing a function. Subsequence compositions of ap
will each provide a task. The success of all those tasks will be given to the initial task which resulted in a function. This is a type safe way of running map
on an arbitrary number of tasks. The function specifies its arguments, which must equal the number of ap
chains. Similar to Task.all
, but with 1 parameter per task, rather than an array, and it works with different task success types, rather than requiring all tasks succeed with the same type.
Also allows the definition of the mapping function to be asychronous because it is also inside a Task.
Without easy function composition in Javascript, for readability we recommend using the instance version .ap
rather than the nested calls the pure function requires.
Last updated