Better integration with async/await
See original GitHub issueHi there,
I know that this has come up a few times in a few different ways, but I’m coming at it from a different point of view.
One of the major benefits of async/await is that it helps to make the code more readable. You get to write more procedural code that is more obvious to read and maintain, instead of complicated call stacks.
For example, the following are the same:
return doSomething()
.then(result => doSomethingElse(result))
.then(next => next.abc);
// -----
const result = await doSomething();
const next = await doSomethingElse(result);
return next.abc;
The second of which is much more obvious for someone to follow.
Neverthrow is awesome, but the fact that it not only doesn’t work with async/await but also doesn’t work well with Promise makes it awkward to use. If I start to use it in a call stack I end up having to use it throughout the entire call stack, or else using things like _unsafeUnwrap()
to escape from it.
This means that it’s more difficult to introduce into a project. I have to start from the outside and work in, because I can’t easily convert a layer until all of the callers are already expecting ResultAsync
objects to be returned. And this is especially big if you consider infrastructure-level layers. For example, if I were to update a database layer to use neverthrow instead of exceptions then I’ve got to update the entire application in order to make that work!
The frustrating thing is that Promise<Result<T, E>>
almost works fine. It’s just the fact that the various methods on it - map
, andThen
, etc - don’t allow for async methods that cause problems.
For example, the following would - to me, at least - be a much easier to understand and maintain alternative:
return doSomething() // function doSomething() : ResultAsync<string, Error>
.andThen(doSomethingElse) // function doSomethingElse(string) : ResultAsync<foo, Error>
.map(next => next.abc());
// -----
const result = await doSomething(); // async function doSomething() : Promise<Result<string, Error>>
const next = await result.andThenAsync(doSomethingElse); // async function doSomethingElse(string) : Promise<Result<foo, Error>>
return await next.mapAsync(n => n.abc());
where in this case the andThenAsync
and mapAsync
functions take async functions and return Promise<Result<T, E>>
instead. And, importantly, it would mean that I can replace any individual part of the call chain with neverthrow without needing to replace all of it.
Issue Analytics
- State:
- Created 2 years ago
- Comments:10 (6 by maintainers)
Top GitHub Comments
Yes, either you want to stick with
async/await
inside the method and maybe return aPromise<Result>
or you can wrap any lower-level async code inside afromPromise
wrapper (which lets you type the error). I would do the latter.All good !
I was using
async
on the function so that I can useawait
within it. And I suspect that in reality, I need to usefromPromise
inside it instead. (This exact function is using Got.post() to make HTTP requests, so was await-ing on the results of that)As for now using
result.value
- that was just an oversight. I’d not realised it was there! Partly because VSCode/TS is “clever” and it’s not even an autocomplete option until after you’ve done theresult.isErr()
bit.Cheers