Let's also assume that you're a developer tasked with leveraging these functions in order to get all of the favorite foods of all of the cats owned by a single user.

By taking a close look at the type signatures of these functions, we can start to see how we might go about implementing our task:

First call getUserFromSession

then get the User and use that value to call getCatsByUserId

then get all of the cats (Cat[]) and call getCatFavoriteFoodsByCatIds by passing it an array of cat ids

The issue is that the values we need (User, Cat[] and Food[]) are wrapped inside of Promise and Result.

First Attempt At A Solution

Let's see how we might implement this naively.

The neverthrow api has a asyncMapmethod and andThenmethod that we could use to solve this:

// imagine we have a sessionId alreadyconstresult1=awaitgetUserFromSessionId(sessionId)// result2 is a Result<Result<Cat[]>, AppError>, AppError>constresult2=awaitresult1.asyncMap((user)=>getCatsByUserId(user.id))// need to get the inner result using `andThen`// now catListResult is Result<Cat[]>, AppError>constcatListResult=result2.andThen((innerResult)=>innerResult)// result3 is// Result<Result<Food[], AppError>, AppError>constresult3=awaitcatListResult.asyncMap((cats)=>getCatFavoriteFoodsByCatIds(cats.map((cat)=>cat.id)))// so now we need to unwrap the inner result again ...// foodListResult is Result<Food[], AppError>constfoodListResult=result3.andThen((innerResult=>innerResult))

Holy boilerplate! That was not fun. And super cumbersome! There was a lot of legwork required to continue this chain of async Result tasks.

... If there were only a better way!

Using Result Chains! 🔗

Version 2.2.0 of neverthrow introduces a wayyy better approach to dealing with this issue.

There are 8 different chain functions, each of which only vary in their arity (the number of arguments that the functions take).

chain: takes 2 async Result tasks

chain3: takes 3 async Result tasks

chain4: takes 4 async Result tasks

chain5: etc

chain6: etc

chain7: etc

chain8: etc

The beautiful thing about this chain API is that it retains the same properties as synchronous Result.map chains ... namely, these async chains short-circuit whenever something at the top of the chain results in a Err value 😍

A useful way to think of the chain api is to think of it as the asynchronous alternative to the andThen method.

I've had this issue noodling in my head for a while. Eventually in that same github issue I mentioned at the top of this post, I proposed an approach to chaining many async computations with a set of utility functions.

Before committing to that solution, I started dogfooding this approach through my own side project. After a few days of using this chain API, I concluded that it was in fact quite good and ergonomic.