Interface: Pending<T, E>
A Task<T, E>
that has not yet resolved.
Extends
Omit
<TaskImpl
<T
,E
>,"value"
|"reason"
>
Type Parameters
T
T
The type of the value when the Task
resolves successfully.
E
E
The type of the rejection reason when the Task
rejects.
Accessors
isPending
Get Signature
get isPending():
true
Returns
true
Overrides
Omit.isPending
isRejected
Get Signature
get isRejected():
false
Returns
false
Overrides
Omit.isRejected
isResolved
Get Signature
get isResolved():
false
Returns
false
Overrides
Omit.isResolved
state
Get Signature
get state():
"Pending"
Returns
"Pending"
Overrides
Omit.state
Methods
and()
and<
U
,F
>(other
):Task
<U
,E
|F
>
You can think of this like a short-circuiting logical "and" operation on a Task
. If this task
resolves, then the output is the task passed to the method. If this task
rejects, the result is its rejection reason.
This is useful when you have another Task
value you want to provide if and only if the first task resolves successfully – that is, when you need to make sure that if you reject, whatever else you're handing a Task
to also gets that Rejected
.
Notice that, unlike in Task.prototype.map
, the original task
resolution value is not involved in constructing the new Task
.
Examples
import Task from 'true-myth/task';
let resolvedA = Task.resolve<string, string>('A');
let resolvedB = Task.resolve<string, string>('B');
let rejectedA = Task.reject<string, string>('bad');
let rejectedB = Task.reject<string, string>('lame');
let aAndB = resolvedA.and(resolvedB);
await aAndB;
let aAndRA = resolvedA.and(rejectedA);
await aAndRA;
let raAndA = rejectedA.and(resolvedA);
await raAndA;
let raAndRb = rejectedA.and(rejectedB);
await raAndRb;
expect(aAndB.toString()).toEqual('Task.Resolved("B")');
expect(aAndRA.toString()).toEqual('Task.Rejected("bad")');
expect(raAndA.toString()).toEqual('Task.Rejected("bad")');
expect(raAndRb.toString()).toEqual('Task.Rejected("bad")');
Type Parameters
U
U
The type of the value for a resolved version of the other
Task
, i.e., the success type of the final Task
present if the first Task
is Ok
.
F
F
= E
Parameters
other
Task
<U
, F
>
The Task
instance to return if this
is Rejected
.
Returns
Task
<U
, E
| F
>
Inherited from
Omit.and
andThen()
Call Signature
andThen<
U
>(thenFn
):Task
<U
,E
>
Apply a function to the resulting value if a Task
is Resolved
, producing a new Task
; or if it is Rejected
return the rejection reason unmodified.
This differs from map
in that thenFn
returns another Task
. You can use andThen
to combine two functions which both create a Task
from an unwrapped type.
The Promise.prototype.then
method is a helpful comparison: if you have a Promise
, you can pass its then
method a callback which returns another Promise
, and the result will not be a nested promise, but a single Promise
. The difference is that Promise.prototype.then
unwraps all layers to only ever return a single Promise
value, whereas this method will not unwrap nested Task
s.
Promise.prototype.then
also acts the same way Task.prototype.map
does, while Task
distinguishes map
from andThen
.
`andThen` is sometimes also known as `bind`, but *not* aliased as
such because bind
already means something in JavaScript.
Examples
import Task from 'true-myth/task';
const toLengthAsResult = (s: string) => ok(s.length);
const aResolvedTask = Task.resolve('just a string');
const lengthAsResult = await aResolvedTask.andThen(toLengthAsResult);
console.log(lengthAsResult.toString()); // Ok(13)
const aRejectedTask = Task.reject(['srsly', 'whatever']);
const notLengthAsResult = await aRejectedTask.andThen(toLengthAsResult);
console.log(notLengthAsResult.toString()); // Err(srsly,whatever)
Type Parameters
U
U
The type of the value produced by the new Task
of the Result
returned by the thenFn
.
Parameters
thenFn
(t
) => Task
<U
, E
>
The function to apply to the wrapped T
if maybe
is Just
.
Returns
Task
<U
, E
>
Inherited from
Omit.andThen
Call Signature
andThen<
R
>(thenFn
):Task
<ResolvesTo
<R
>,E
|RejectsWith
<R
>>
Apply a function to the resulting value if a Task
is Resolved
, producing a new Task
; or if it is Rejected
return the rejection reason unmodified.
This differs from map
in that thenFn
returns another Task
. You can use andThen
to combine two functions which both create a Task
from an unwrapped type.
The Promise.prototype.then
method is a helpful comparison: if you have a Promise
, you can pass its then
method a callback which returns another Promise
, and the result will not be a nested promise, but a single Promise
. The difference is that Promise.prototype.then
unwraps all layers to only ever return a single Promise
value, whereas this method will not unwrap nested Task
s.
Promise.prototype.then
also acts the same way Task.prototype.map
does, while Task
distinguishes map
from andThen
.
`andThen` is sometimes also known as `bind`, but *not* aliased as
such because bind
already means something in JavaScript.
Examples
import Task from 'true-myth/task';
const toLengthAsResult = (s: string) => ok(s.length);
const aResolvedTask = Task.resolve('just a string');
const lengthAsResult = await aResolvedTask.andThen(toLengthAsResult);
console.log(lengthAsResult.toString()); // Ok(13)
const aRejectedTask = Task.reject(['srsly', 'whatever']);
const notLengthAsResult = await aRejectedTask.andThen(toLengthAsResult);
console.log(notLengthAsResult.toString()); // Err(srsly,whatever)
Type Parameters
R
R
extends AnyTask
Parameters
thenFn
(t
) => R
The function to apply to the wrapped T
if maybe
is Just
.
Returns
Task
<ResolvesTo
<R
>, E
| RejectsWith
<R
>>
Inherited from
Omit.andThen
map()
map<
U
>(mapFn
):Task
<U
,E
>
Map over a Task
instance: apply the function to the resolved value if the task completes successfully, producing a new Task
with the value returned from the function. If the task failed, return the rejection as Rejected
without modification.
map
works a lot like Array.prototype.map
, but with one important difference. Both Task
and Array
are kind of like a “container” for other kinds of items, but where Array.prototype.map
has 0 to n items, a Task
represents the possibility of an item being available at some point in the future, and when it is present, it is either a success or an error.
Where Array.prototype.map
will apply the mapping function to every item in the array (if there are any), Task.map
will only apply the mapping function to the resolved element if it is Resolved
.
If you have no items in an array of numbers named foo
and call foo.map(x => x + 1)
, you'll still some have an array with nothing in it. But if you have any items in the array ([2, 3]
), and you call foo.map(x => x + 1)
on it, you'll get a new array with each of those items inside the array "container" transformed ([3, 4]
).
With this map
, the Rejected
variant is treated by the map
function kind of the same way as the empty array case: it's just ignored, and you get back a new Task
that is still just the same Rejected
instance. But if you have an Resolved
variant, the map function is applied to it, and you get back a new Task
with the value transformed, and still Resolved
.
Examples
import Task from 'true-myth/task';
const double = n => n * 2;
const aResolvedTask = Task.resolve(12);
const mappedResolved = aResolvedTask.map(double);
let resolvedResult = await aResolvedTask;
console.log(resolvedResult.toString()); // Ok(24)
const aRejectedTask = Task.reject("nothing here!");
const mappedRejected = aRejectedTask.map(double);
let rejectedResult = await aRejectedTask;
console.log(rejectedResult.toString()); // Err("nothing here!")
Type Parameters
U
U
The type of the resolved value of the returned Task
.
Parameters
mapFn
(t
) => U
The function to apply the value to when the Task
finishes if it is Resolved
.
Returns
Task
<U
, E
>
Inherited from
Omit.map
mapRejected()
mapRejected<
F
>(mapFn
):Task
<T
,F
>
Map over a Task
, exactly as in map
, but operating on the rejection reason if the Task
rejects, producing a new Task
, still rejected, with the value returned from the function. If the task completed successfully, return it as Resolved
without modification. This is handy for when you need to line up a bunch of different types of errors, or if you need an error of one shape to be in a different shape to use somewhere else in your codebase.
Examples
import Task from 'true-myth/task';
const extractReason = (err: { code: number, reason: string }) => err.reason;
const aResolvedTask = Task.resolve(12);
const mappedResolved = aResolvedTask.mapRejected(extractReason);
console.log(mappedOk)); // Ok(12)
const aRejectedTask = Task.reject({ code: 101, reason: 'bad file' });
const mappedRejection = await aRejectedTask.mapRejected(extractReason);
console.log(toString(mappedRejection)); // Err("bad file")
Type Parameters
F
F
The type of the rejection for the new Task
, returned by the mapFn
.
Parameters
mapFn
(e
) => F
The function to apply to the rejection reason if the Task
is rejected.
Returns
Task
<T
, F
>
Inherited from
Omit.mapRejected
match()
match<
A
>(matcher
):Promise
<A
>
Allows you to produce a new value by providing functions to operate against both the Resolved
and Rejected
states once the Task
resolves.
(This is a workaround for JavaScript’s lack of native pattern-matching.)
Example
import Task from 'true-myth/task';
let theTask = new Task<number, Error>((resolve, reject) => {
let value = Math.random();
if (value > 0.5) {
resolve(value);
} else {
reject(new Error(`too low: ${value}`));
}
});
// Note that we are here awaiting the `Promise` returned from the `Task`,
// not the `Task` itself.
await theTask.match({
Resolved: (num) => {
console.log(num);
},
Rejected: (err) => {
console.error(err);
},
});
This can both be used to produce side effects (as here) and to produce a value regardless of the resolution/rejection of the task, and is often clearer than trying to use other methods. Thus, this is especially convenient for times when there is a complex task output.
NOTE
You could also write the above example like this, taking advantage of how awaiting a Task
produces its inner Result
:
import Task from 'true-myth/task';
let theTask = new Task<number, Error>((resolve, reject) => {
let value = Math.random();
if (value > 0.5) {
resolve(value);
} else {
reject(new Error(`too low: ${value}`));
}
});
let theResult = await theTask;
theResult.match({
Ok: (num) => {
console.log(num);
},
Err: (err) => {
console.error(err);
},
});
Which of these you choose is a matter of taste!
Type Parameters
A
A
Parameters
matcher
Matcher
<T
, E
, A
>
A lightweight object defining what to do in the case of each variant.
Returns
Promise
<A
>
Inherited from
Omit.match
or()
or<
F
,U
>(other
):Task
<T
|U
,F
>
Provide a fallback for a given Task
. Behaves like a logical or
: if the task
value is Resolved
, returns that task
unchanged, otherwise, returns the other
Task
.
This is useful when you want to make sure that something which takes a Task
always ends up getting a Resolved
variant, by supplying a default value for the case that you currently have an Rejected
.
import Task from 'true-utils/task';
const resolvedA = Task.resolve<string, string>('a');
const resolvedB = Task.resolve<string, string>('b');
const rejectedWat = Task.reject<string, string>(':wat:');
const rejectedHeaddesk = Task.reject<string, string>(':headdesk:');
console.log(resolvedA.or(resolvedB).toString()); // Resolved("a")
console.log(resolvedA.or(rejectedWat).toString()); // Resolved("a")
console.log(rejectedWat.or(resolvedB).toString()); // Resolved("b")
console.log(rejectedWat.or(rejectedHeaddesk).toString()); // Rejected(":headdesk:")
Type Parameters
F
F
The type wrapped in the Rejected
case of other
.
U
U
= T
Parameters
other
Task
<U
, F
>
The Result
to use if this
is Rejected
.
Returns
Task
<T
| U
, F
>
this
if it is Resolved
, otherwise other
.
Inherited from
Omit.or
orElse()
Call Signature
orElse<
F
>(elseFn
):Task
<T
,F
>
Like or
, but using a function to construct the alternative Task
.
Sometimes you need to perform an operation using the rejection reason (and possibly also other data in the environment) to construct a new Task
, which may itself resolve or reject. In these situations, you can pass a function (which may be a closure) as the elseFn
to generate the fallback Task<T, E>
. It can then transform the data in the Rejected
to something usable as an Resolved
, or generate a new Rejected
instance as appropriate.
Useful for transforming failures to usable data, for trigger retries, etc.
Type Parameters
F
F
Parameters
elseFn
(reason
) => Task
<T
, F
>
The function to apply to the Rejection
reason if the Task
rejects, to create a new Task
.
Returns
Task
<T
, F
>
Inherited from
Omit.orElse
Call Signature
orElse<
R
>(elseFn
):Task
<T
|ResolvesTo
<R
>,RejectsWith
<R
>>
Like or
, but using a function to construct the alternative Task
.
Sometimes you need to perform an operation using the rejection reason (and possibly also other data in the environment) to construct a new Task
, which may itself resolve or reject. In these situations, you can pass a function (which may be a closure) as the elseFn
to generate the fallback Task<T, E>
. It can then transform the data in the Rejected
to something usable as an Resolved
, or generate a new Rejected
instance as appropriate.
Useful for transforming failures to usable data, for trigger retries, etc.
Type Parameters
R
R
extends AnyTask
Parameters
elseFn
(reason
) => R
The function to apply to the Rejection
reason if the Task
rejects, to create a new Task
.
Returns
Task
<T
| ResolvesTo
<R
>, RejectsWith
<R
>>
Inherited from
Omit.orElse
then()
then<
A
,B
>(onSuccess?
,onRejected?
):PromiseLike
<A
|B
>
Attaches callbacks for the resolution and/or rejection of the Promise.
Type Parameters
A
A
B
B
Parameters
onSuccess?
(result
) => A
| PromiseLike
<A
>
onRejected?
(reason
) => B
| PromiseLike
<B
>
Returns
PromiseLike
<A
| B
>
A Promise for the completion of which ever callback is executed.
Inherited from
Omit.then
timeout()
Attempt to run this Task
to completion, but stop if the passed Timer
, or one constructed from a passed time in milliseconds, elapses first.
If this Task
and the duration happen to have the same duration, timeout
will favor this Task
over the timeout.
Parameters
timerOrMs
A Timer
or a number of milliseconds to wait for this task before timing out.
number
| Timer
Returns
A Task
which has the resolution value of this
or a Timeout
if the timer elapsed.
Inherited from
Omit.timeout
toPromise()
toPromise():
Promise
<Result
<T
,E
>>
Get the underlying Promise
. Useful when you need to work with an API which requires a Promise
, rather than a PromiseLike
.
Note that this maintains the invariants for a Task
up till the point you call this function. That is, because the resulting promise was managed by a Task
, it always resolves successfully to a Result
. However, calling then then
or catch
methods on that Promise
will produce a new Promise
for which those guarantees do not hold.
IMPORTANT
If the resulting Promise
ever rejects, that is a BUG, and you should open an issue so we can fix it!
Returns
Promise
<Result
<T
, E
>>
Inherited from
Omit.toPromise
toString()
toString():
string
Returns a string representation of an object.
Returns
string
Inherited from
Omit.toString