Home > Enterprise >  How use Either with async function
How use Either with async function

Time:02-04

I'm learning fp-ts now and try to replace some parts of my API with its approach.

Simple example:

  • get itemId with request
  • use this itemId with async function to get (or not) the item

The only way I found is:

 const itemId: E.Either<string, Item["id"]> = pipe(
    body.itemId,
    E.fromNullable('Item ID is missed')
 );
 
 const item: E.Either<string, Item> = E.isLeft(itemId)
    ? itemId
    : pipe(
      await getItem(itemId.right),
      E.fromNullable('cant find item')
    );

It works. But is it right way? Is it any way to do it all in one pipe function? Thank you.

CodePudding user response:

There are a few layers to this question so I'll present a possible solution and walk through how it works.

First, fp-ts uses Tasks to represent asynchronous work, so most of the time when working with async code in fp-ts you first convert the code to a Task or TaskEither (more on that in a second). First, we need a function to perform the getItem task and return an Either instead of null when null is returned. This can be defined as:

import * as TE from 'fp-ts/lib/TaskEither';
import * as T from 'fp-ts/lib/Task';
// flow is function composition and avoids some syntax.
// This can be thought of a "point-free" although internally I use a
// not point-free function.
const getItemTE: (id: number) => TE.TaskEither<string, Item> = flow(
  getItem,
  // A task must be a function with no arguments that returns a promise,
  // This takes the result of calling getItem and returns a function that
  // returns that response in order to match the Task interface
  (promisedItem) => () => promisedItem,
  // We convert that task into a `TaskEither`, to get the type we want,
  // but note that this value will never be `left` because the underlying
  // task was returning `Item | null`. Both of those values are considered
  // a Right.
  TE.fromTask,
  // Converting to a TaskEither allows us to chain, which will hand us the
  // unwrapped `right` value and expects us to return a new TaskEither.
  // In this case, we want the task either we get by converting null
  // values into Eithers using the `fromNullable` function.
  TE.chain(TE.fromNullable("can't find item")),
);

Now we have a function that takes a number and produces a TaskEither which will fetch the item.

Next you want to use this function with your body object. You can use the newly created function along with body to get an item:

// Note: at this point the Promise will be created so
// any IO is going to be kicked off here, not when the value
// is eventually awaited.
const item: TE.TaskEither<string, Item> = pipe(
  body.itemId,
  // This will wrap up the value into a task either
  // which enables us to chain.
  TE.fromNullable('Item id is missed'),
  // Now we just chain with the newly created function.
  // If the value is `right` then the value is fetched,
  // otherwise the 'Item id is missed' value is stored
  // in left and is returned.
  TE.chain(getItemTE)
);

Finally, you can do something with the value as follows:

async function doSomethingWithItem(item: TE.TaskEither<string, Item>) {
  pipe(
    // The item will already have been fetched at this point due
    // to the way the code is written. If you want to avoid fetching
    // the object right away, you might need to change around
    // the line where I went from the `getItem` response to a Task
    await item(),
    E.fold(
      (err) => console.error(err),
      (item) => {
        console.log(`Got item with id: ${item.itemId}`);
      },
    )
  );
}

doSomethingWithItem(item);
  • Related