Treating async operations as first-class citizens
See original GitHub issueHi and thanks for writing this cool library ! I’ve been looking at monad libraries for a while now and they all seamed unnecessarily complex to me. I came to the conclusion that the main (only?) benefit of Either types was to have typed Errors. When I read your blog article, I thought “right on !”.
There is one thing that bugs me though and it has to do with handling async operations.
Your (great) library offers a Result
type with a bunch of great api methods like map, mapErr, andThen, match, etc. but as soon as an operation is async, there aren’t as many options.
I like the chain
solution but it doesn’t offer the same chainability as we would get with the synchronous Result
.
I understand this has to do with the fact that you don’t have control over the api of Promise<Result>
but I believe there might be an elegant solution: having a wrapper around that Promise. A ResultAsync<T>
type that would have more or less the same api as Result<T>
and could offer full chainability.
Furthermore, I was thinking that whatever the Result
could do, the ResultAsync
could do too. Therefor, I’m not even sure we need both types, only the async version. In the Real World, there’s always an async operation somewhere in the chain anyway, right ?
The method signatures would look something like this:
map<U>(fn: T => U | T => Promise<U>): ResultAsync<U, E>
mapErr<U>(fn: E => F | E => Promise<F>): ResultAsync <T, F>
andThen<U>(fn: T => ResultAsync <U,E>): ResultAsync <U, E>
match<A>(
okFn: (t: T) => A,
errFn: (e: E) => A
): Promise<A>
then(cb: (T | E) => any) // a method to make the ResultAsync a thenable which can have the result extracted by calling either .then() or await
And a code example:
const newUserId = await api.checkAvailability()
.andThen(api.createNewUser)
.map({ id } => id);
// newUserId is of type string | Error
if(newUserId instanceof Error){
// newUserId is an Error
console.log(newUserId.message);
}
else{
// newUserId is a string
console.log("new user id is ", newUserId)
}
Thanks again and looking forward to your thoughts !
Issue Analytics
- State:
- Created 3 years ago
- Comments:6 (6 by maintainers)
Top GitHub Comments
Sounds good. As long as it’s completely non-breaking!
No problem !
Making
Result
awaitable
would indeed introduce a breaking change becauseawait
would be necessary to access the underlying value or error.I’m moving forward with a new
ResultAsync
(with it’s own file and tests) that mostly duplicates theResult
API. I thinkResult.asyncMap
would return aResultAsync
instead of the currentPromise<Result<U,E>>
so we could continue chaining.