【问题标题】:Chain some async tasks in fp-ts retaining every task's result在 fp-ts 中链接一些异步任务,保留每个任务的结果
【发布时间】:2020-02-21 21:53:09
【问题描述】:

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

main1 函数是针对此问题的async/await 式解决方案。我想要做的是在 fp-ts chain-style 中模拟这样的东西。 main2 是我的尝试。

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

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

我认为使这个版本工作的唯一方法是让异步函数本身(即getFoogetBar返回它们的参数,或者可能是@987654333 @wrappers 返回参数,以便它们可以传递给链中的下一个函数。

这是正确的方法吗?还是有更简单的版本更接近async/await 版本?

【问题讨论】:

    标签: typescript fp-ts


    【解决方案1】:

    根据您需要在以下计算中访问中间结果的次数,我建议您使用Do(Haskell 的 do 表示法的近似值),或者通过手动传递中间结果 map平。

    给定:

    import { pipe } from "fp-ts/function";
    import * as TE from "fp-ts/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>;
    

    Do 为例:

    function main2(): TE.TaskEither<unknown, FooBar> {
      return pipe(
        TE.Do,
        TE.bind("foo", () => getFoo("a")),
        TE.bind("bar", ({ foo }) => getBar(foo)),
        TE.chain(({ foo, bar }) => mkFooBar(foo, bar))
      );
    }
    

    手动映射示例:

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

    【讨论】:

    • 此解决方案应作为示例包含在 fp-ts 文档中。在学习 fp-ts 时,通过管道链接任务一开始并不容易,这个答案涵盖了许多基本用法。
    • @zenbeni 你见过gcanti.github.io/fp-ts/guides/do-notation.html吗?它应该已经涵盖了 Do 符号。我也刚刚更新了使用 Do from fp-ts 而不是 fp-ts-contrib 的答案,在较新的版本中可用
    猜你喜欢
    • 2023-03-27
    • 2014-08-06
    • 1970-01-01
    • 2016-01-16
    • 1970-01-01
    • 1970-01-01
    • 2013-12-27
    • 2011-11-21
    • 1970-01-01
    相关资源
    最近更新 更多