在 fp-ts 中链接一些异步任务,保留每个任务的结果
Chain some async tasks in fp-ts retaining every task's result
在 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
风格中模拟类似的东西。 main2
是我的尝试。
因为async/await
版本将所有的中间结果都引入了本地作用域(即foo
和bar
),所以调用mkFooBar
很容易,这取决于两者这些结果。
但在fp-ts版本中,中间结果被困在每个任务的范围内。
我认为使这个版本工作的唯一方法是使异步函数本身(即 getFoo
和 getBar
) 也 return 它们的参数,或者 TaskEither
包装器 return 参数,以便它们可以传递到链中的下一个函数。
这是执行此操作的正确方法吗?或者是否有更简单的版本更类似于 async/await
版本?
根据您需要在以下计算中访问中间结果的次数,我建议使用 Do
(Haskell 的近似表示法),或者通过手动 map
ping.
携带中间结果
鉴于:
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 中,我试图将一些可能失败的异步任务与 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
风格中模拟类似的东西。 main2
是我的尝试。
因为async/await
版本将所有的中间结果都引入了本地作用域(即foo
和bar
),所以调用mkFooBar
很容易,这取决于两者这些结果。
但在fp-ts版本中,中间结果被困在每个任务的范围内。
我认为使这个版本工作的唯一方法是使异步函数本身(即 getFoo
和 getBar
) 也 return 它们的参数,或者 TaskEither
包装器 return 参数,以便它们可以传递到链中的下一个函数。
这是执行此操作的正确方法吗?或者是否有更简单的版本更类似于 async/await
版本?
根据您需要在以下计算中访问中间结果的次数,我建议使用 Do
(Haskell 的近似表示法),或者通过手动 map
ping.
鉴于:
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))
);
}