在 fp-ts 中链接一些异步任务,保留每个任务的结果

iro*_*ken 10 typescript fp-ts

在 fp-ts 中,我试图将一些可能失败的异步任务链接在一起,TaskEither但我需要稍后在链中使用中间任务的结果。

在这个例子中:

const getFoo = (a: string): Promise<Foo> => {};
const getBar = (foo: Foo): Promise<Bar> => {};
const mkFooBar = (foo: Foo, bar: Bar): Promise<FooBar> => {};

const async main1: Promise<FooBar> => {
  const a = "a";
  const foo = await getFoo(a);
  const bar = await getBar(foo);
  const fooBar = await mkFooBar(foo, bar);

  return Promise.resolve(fooBar);
};

const main2: Promise<FooBar> => {
  const a = "a";

  return pipe(
    TE.tryCatch(() => getFoo(a), e => e),
    TE.chain(foo => TE.tryCatch(() => getBar(foo), e => e)),
    TE.chain(bar => TE.tryCatch(() => mkFooBar(??, bar), e => e))
  );
};
Run Code Online (Sandbox Code Playgroud)

main1函数是async/await这个问题的一个风格的解决方案。我想要做的是以 fp-tschain风格模拟这样的事情。main2是我在这方面的尝试。

因为该async/await版本将所有中间结果引入本地范围(即foobar),所以很容易调用mkFooBar它取决于这两个结果。

但是在 fp-ts 版本中,中间结果被困在每个任务的范围内。

我能想到的让这个版本工作的唯一方法是让异步函数本身(即getFoogetBar返回它们的参数,或者TaskEither包装器返回参数,以便它们可以被传递到下一个函数链。

这是做到这一点的正确方法吗?或者是否有更简单的版本与版本更相似async/await

Gio*_*aga 16

根据您在以下计算中需要访问中间结果的次数,我建议要么使用Dofromfp-ts-contrib(Haskell do 符号的近似值),要么通过手动mapping传递中间结果。

鉴于:

import * as TE from "fp-ts/lib/TaskEither";

declare function getFoo(a: string): TE.TaskEither<unknown, Foo>;
declare function getBar(foo: Foo): TE.TaskEither<unknown, Bar>;
declare function mkFooBar(foo: Foo, bar: Bar): TE.TaskEither<unknown, FooBar>;
Run Code Online (Sandbox Code Playgroud)

示例Do

import { Do } from "fp-ts-contrib/lib/Do";

function main2(): TE.TaskEither<unknown, FooBar> {
  return Do(TE.taskEither)
    .bind("foo", getFoo("a"))
    .bindL("bar", ({ foo }) => getBar(foo))
    .bindL("fooBar", ({ foo, bar }) => mkFooBar(foo, bar))
    .return(({ fooBar }) => fooBar);
}
Run Code Online (Sandbox Code Playgroud)

手动映射示例:

import { pipe } from "fp-ts/lib/pipeable";

function main3(): TE.TaskEither<unknown, FooBar> {
  return pipe(
    getFoo("a"),
    TE.chain(foo =>
      pipe(
        getBar(foo),
        TE.map(bar => ({ foo, bar }))
      )
    ),
    TE.chain(({ foo, bar }) => mkFooBar(foo, bar))
  );
}
Run Code Online (Sandbox Code Playgroud)