typescript 如何将Either与异步函数一起使用

jfewjypa  于 2023-02-05  发布在  TypeScript
关注(0)|答案(1)|浏览(104)

我现在正在学习fp-ts,并尝试用它的方法替换我的API的某些部分。
简单举例:

  • 使用请求获取itemId
  • 使用此itemId和async函数来获取(或不获取)项目

我找到的唯一办法是:

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')
    );

它可以工作。但是它的方式正确吗?它有没有办法在一个pipe函数中完成这一切?谢谢。

whlutmcx

whlutmcx1#

这个问题有几个层次,所以我将提出一个可能的解决方案,并介绍它是如何工作的。
首先,fp-ts使用Task来表示异步工作,因此在fp-ts中使用异步代码时,大多数情况下您首先将代码转换为TaskTaskEither(稍后会详细介绍)。首先,我们需要一个函数来执行getItem任务,并且在返回null时返回Either而不是null

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")),
);

现在我们有了一个函数,它接受一个数字并产生一个TaskEither,它将获取该项目。
接下来,我们要对body对象使用这个函数,可以将新创建的函数与body沿着使用来获取一个项:

// 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)
);

最后,您可以对该值执行一些操作,如下所示:

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);

相关问题