I asked some days before about Promise.allSettled, but I have the same question for Promise.race.
Let's say I have an array of
declare const myTEs: TE.TaskEither<E,A>[];
How would one implement a Promise.race? It should return TE.TaskEither<E,A>.
I think this example could go on this page: https://gcanti.github.io/fp-ts/recipes/async.html
I would go with:
import { fold } from 'fp-ts/lib/Monoid'
import { getRaceMonoid } from 'fp-ts/lib/Task'
fold(getRaceMonoid<Either<E, A>>())(myTEs)
We could add to the docs a table similar to:
| | Promise | Task | TaskEither |
| --- | --- | --- | --- |
| resolve to success | Promise.resolve(value) | T.task.of(value) | TE.taskEither.of(value) or TE.right(value) |
| resolve to failure | Promise.reject(value) | N/A | TE.left(value) |
| transform the result of a task with the function f | promise.then(f) | T.task.map(task, f) | T.taskEither.map(taskEither, f) |
| perform a task depending on the result of a previous one | promise.then(r => getPromise(r)) | T.task.chain(task, r => getTask(r)) | T.taskEither.chain(taskEither, r => getTaskEither(r)) |
| execute an array of tasks in parallel | Promise.all(promises) | A.array.sequence(T.task)(tasks) | A.array.sequence(TE.taskEither)(taskEithers) |
| execute an array of tasks in parallel, collecting all failures and successes | Promise.allSettled(promises) | N/A | A.array.sequence(T.task)(taskEithers) |
| execute an array of tasks and succeed/fail with a single value as soon as one of the tasks succeeds/fails | Promise.race(promises) | fold(T.getRaceMonoid())(tasks) | fold(T.getRaceMonoid())(taskEithers) |
How would one implement a Promise.any behavior (still in proposal) with fp-ts? Basically, given n Promises, resolve when the first of them resolves, or reject if all promises reject.
@amaurymartiny precisely what would be the signature?
Same as Promise.race:
declare function f<E,A>(tes: TE.TaskEither<E,A>[]): TE.TaskEither<E,A>
What's that E in the return type?
or rejected with an AggregateError holding the rejection reasons if all of the given promises are rejected
Shouldn't be Array<E>?
My mistake, you're right, TE.TaskEither<E[],A> as the return type
What if tes is an empty array? The proposal doesn't seem to specify the expected behavior.
AFAIK there are two possible behaviors:
TaskEither<NonEmptyArray<E>, A>)Array<E>Another option is to change the whole signature to:
declare function f<E, A>(tes: NonEmptyArray<TE.TaskEither<E, A>>): TE.TaskEither<NonEmptyArray<E>, A>
See https://github.com/tc39/proposal-promise-any/issues/17.
It seems that it should be: "the return task resolves with an empty Array
Another option is to change the whole signature to:
I'd stick as close as possible to the proposal, for new people transitioning to fp-ts.
ok, so you could wrap a polyfill
import * as E from 'fp-ts/lib/Either'
import * as TE from 'fp-ts/lib/TaskEither'
declare global {
interface PromiseConstructor {
any<A>(promises: Array<Promise<A>>): Promise<A>
}
}
function toPromise<E, A>(te: TE.TaskEither<E, A>): Promise<A> {
return te().then(
E.fold(
e => Promise.reject(e),
a => Promise.resolve(a)
)
)
}
export function any<E, A>(tes: Array<TE.TaskEither<E, A>>): TE.TaskEither<Array<E>, A> {
return () => Promise.any(tes.map(toPromise)).then(E.right, E.left)
}
I would have imagined there existed a pure fp-ts way of reaching this behavior, without using a Promise.any polyfill.
All promise methods in giogonzo's table can be done with the above code and the correct polyfill, the interesting part (for me, as a fp learner) is to only use functional programming structures.
to only use functional programming structures
Not sure what you mean, all promise methods in giogonzo's table are actually done with native implementations and polyfill, consider that under the hood:
T.task.of uses Promise.resolveT.task.map uses Promise#thenT.task.ap uses Promise.allT.task.chain uses Promise#thenT.getRaceMonoid uses Promise.race
Most helpful comment
We could add to the docs a table similar to:
| | Promise | Task | TaskEither |
| --- | --- | --- | --- |
| resolve to success |
Promise.resolve(value)|T.task.of(value)|TE.taskEither.of(value)orTE.right(value)|| resolve to failure |
Promise.reject(value)| N/A |TE.left(value)|| transform the result of a task with the function
f|promise.then(f)|T.task.map(task, f)|T.taskEither.map(taskEither, f)|| perform a task depending on the result of a previous one |
promise.then(r => getPromise(r))|T.task.chain(task, r => getTask(r))|T.taskEither.chain(taskEither, r => getTaskEither(r))|| execute an array of tasks in parallel |
Promise.all(promises)|A.array.sequence(T.task)(tasks)|A.array.sequence(TE.taskEither)(taskEithers)|| execute an array of tasks in parallel, collecting all failures and successes |
Promise.allSettled(promises)| N/A |A.array.sequence(T.task)(taskEithers)|| execute an array of tasks and succeed/fail with a single value as soon as one of the tasks succeeds/fails |
Promise.race(promises)|fold(T.getRaceMonoid())(tasks)|fold(T.getRaceMonoid())(taskEithers)|