question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Treating async operations as first-class citizens

See original GitHub issue

Hi 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:closed
  • Created 3 years ago
  • Comments:6 (6 by maintainers)

github_iconTop GitHub Comments

1reaction
supermacrocommented, May 12, 2020

I think Result.asyncMap would return a ResultAsync instead of the current Promise<Result<U,E>> so we could continue chaining.

Sounds good. As long as it’s completely non-breaking!

0reactions
paduccommented, May 12, 2020

No problem !

Making Result awaitable would indeed introduce a breaking change because await 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 the Result API. I think Result.asyncMap would return a ResultAsync instead of the current Promise<Result<U,E>> so we could continue chaining.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Asynchronous values as first-class citizens in Swift
In this post, I'll take a look at asynchronous operations. I specifically like a particular approach towards this: Futures. I like them so...
Read more >
JavaScript function are First class citizen - Dot Net Tutorials
In JavaScript, functions are treated as first-class objects or first-class citizens, because they have the properties and methods the same as other objects....
Read more >
How to treat Errors as first-class citizens in Flux (and Redux)
Treating an error as first-class means that we treat it like all other payload data in our actions. It means we don't need...
Read more >
React RFC: First class support for promises and async/await
There, async/await works just fine. I can guarantee that people are going to forget to wrap their promises in use() and I won't...
Read more >
Node Hero - Understanding Async Programming in Node.js
Asynchronous programming – as we know now in JavaScript – can only be achieved with functions being first-class citizens of the language: ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found