-
Notifications
You must be signed in to change notification settings - Fork 135
Working with ResultAsync
Result Async can be used to work with Asynchronous Results in a typesafe way.
Similarly to Result<T,E>, which contains the Result of a synchronous operation that may have failed, you can represent the result of async operations with ResultAsync<T,E>.
Neverthrow exposes a util that wraps a Promise into an ResultAsync for you.
One, very typical, use-case for this is to handle results that Prisma(or any other ORM) returns from your Database.
const userPromise = prisma.user.findUnique(...)
const result = fromPromise(userPromise, err => ({err, message: "Some error happened while reading DB"}))Notice that as a second Parameter to fromPromise we are passing a function that accepts (error: unknown) and returns a new error object we created.
That way we can control the type of the Error in ResultAsync<T, Error>
We will never use Promises native .catch function to catch errors. This is exactly the responsibility we are fulfilling by using ResultAsync
Similarly to Promise.catch() we are not going to await promises and try to catch errors.
If you find yourself wanting to mark a function, that deals with ResultAsync, as async you probably have a misunderstanding about what your function should do
and how it should handle the asynchronous action.
Below are three functions. Each of them handling the same error in a different way
const callbackUser = () => {
const userPromise = prisma.user.findUnique(...)
userPromise
.then(user => console.log("Found my user", user))
.catch(err => console.error("Error while reading user", err))
}
const asyncAwaiter = async () => {
try {
const user = await prisma.user.findUnique(...)
console.log("Found my user", user)
} catch (err) {
console.error("Error while reading user", err)
}
}
const neverthrower = () => {
const userPromise = prisma.user.findUnique(...)
const result = fromPromise(userPromise, originalError => ({originalError, message: "Error while reading user"}))
result.match(
user => console.log("Found my user", user),
err => console.log(err.message, err.originalError)
)
}Notice that typically you would not read the result of ResultAsync immediately after wrapping it, as it does not much other than bloating up your function a bit more.
The benefit of using this paradigm really comes to the surface when you start to chaining different Result and ResultAsnyc together with the .map and .andThen functions. This enables you to unwrap (with match, or map & mapErr) the Results at a centralized point to outsource the responsibility to react to Errors and Successes.
References: