执行多个反应操作的正确方法
Correct way to execute multiple reactive operations
我从一个反应性存储库中得到一个 Mono<FooBar>
基于它的价值我必须创建另外两个对象使用反应性存储库保存它们,修改 FooBar
对象并保存它。
由于我是反应式编程的新手,所以我找到了以下解决方案,它正在运行,但我不确定我是否正确使用了反应式 API:
@Test
void createAndSave() {
Mono<FooBar> fooBarMono = findFooBar() // returns Mono<FooBar>
.map(fooBar -> {
createAndSaveLoremBar(fooBar).subscribe(); // returns Mono<LoremBar>
createAndSaveDoloremBar(fooBar).subscribe(); // returns Mono<DoloremBar>
fooBar.setActive(true);
return saveFooBar(fooBar); // returns Mono<FooBar>
}).flatMap(Function.identity());
StepVerifier.create(fooBarMono)
.expectNextMatches(Objects::nonNull)
.expectComplete()
.verify();
}
来自控制台日志:
saved lorem bar
saved dolorem bar
saved foo bar
起初,在异步(反应)世界中改变对象并不是一个好主意。
无论如何,在您的解决方案中,有关 lorem 和 dolorem 保存的可能错误将被忽略。你可以这样改进它:
Mono<FooBar> fooBarMono = findFooBar()
.flatMap(fooBar -> Flux.merge(
createAndSaveLoremBar(fooBar),
createAndSaveDoloremBar(fooBar)) // asynchronously saving lorem and dolorem
.then(Mono.fromCallable(() -> { // if there wasn't errors, mutate and save fooBar
fooBar.setActive(true);
return fooBar;
}).flatMap(fooBar1 -> saveFooBar(fooBar1))));
如果您可以使用 true active
标志创建 fooBar
的副本,代码可能会更简单。例如龙目岛。
@Builder(toBuilder = true)
public class FooBar {
...
}
Mono<FooBar> fooBarMono = findFooBar()
.flatMap(fooBar -> Flux.merge(
createAndSaveLoremBar(fooBar),
createAndSaveDoloremBar(fooBar))
.then(saveFooBar(fooBar.toBuilder().active(true).build())));
如果您对 saveFooBar(...)
的结果不感兴趣,而只对完成信号感兴趣,则可以异步进行所有三个保存:
Flux<Object> flux = findFooBar()
.flatMapMany(fooBar -> Flux.merge(
createAndSaveLoremBar(fooBar),
createAndSaveDoloremBar(fooBar),
saveFooBar(fooBar.toBuilder().active(true).build())));
实际上,在最后一种方法中,您可以收集所有三个结果,您应该更喜欢这种方法,但我没有足够的信息来了解您的 类 以及创建完整示例的要求。
我认为下面的解决方案更具可读性。无论如何亚历山大是正确的,你永远不应该修改输入。你看我们借用了很多概念形式的函数式编程。例如你调用 Function.identity()
这个叫做
identity functor
。 Flux
和 Mono
都是单子。有一个对这些人来说是秘密的概念,叫做 Referential transparency
命令更新中断。
final Mono<FooBar> fooBarMono1 = findFooBar()
.zipWhen((fooBar) -> createAndSaveLoremBar(fooBar))
.map(tuple -> tuple.getT1())
.zipWhen((fooBar) -> createAndSaveDoloremBar(fooBar))
.map(tuple -> tuple.getT1())
.map(fooBar -> new FooBar(true))
.flatMap(fooBar -> saveFooBar(fooBar));
或者更简洁:
final Mono<FooBar> fooBarMono1 = findFooBar()
.zipWhen((fooBar) -> createAndSaveLoremBar(fooBar)
.then(createAndSaveDoloremBar(fooBar)))
.map(tuple -> tuple.getT1())
.map(fooBar -> new FooBar(true))
.flatMap(fooBar -> saveFooBar(fooBar));
我从一个反应性存储库中得到一个 Mono<FooBar>
基于它的价值我必须创建另外两个对象使用反应性存储库保存它们,修改 FooBar
对象并保存它。
由于我是反应式编程的新手,所以我找到了以下解决方案,它正在运行,但我不确定我是否正确使用了反应式 API:
@Test
void createAndSave() {
Mono<FooBar> fooBarMono = findFooBar() // returns Mono<FooBar>
.map(fooBar -> {
createAndSaveLoremBar(fooBar).subscribe(); // returns Mono<LoremBar>
createAndSaveDoloremBar(fooBar).subscribe(); // returns Mono<DoloremBar>
fooBar.setActive(true);
return saveFooBar(fooBar); // returns Mono<FooBar>
}).flatMap(Function.identity());
StepVerifier.create(fooBarMono)
.expectNextMatches(Objects::nonNull)
.expectComplete()
.verify();
}
来自控制台日志:
saved lorem bar
saved dolorem bar
saved foo bar
起初,在异步(反应)世界中改变对象并不是一个好主意。
无论如何,在您的解决方案中,有关 lorem 和 dolorem 保存的可能错误将被忽略。你可以这样改进它:
Mono<FooBar> fooBarMono = findFooBar()
.flatMap(fooBar -> Flux.merge(
createAndSaveLoremBar(fooBar),
createAndSaveDoloremBar(fooBar)) // asynchronously saving lorem and dolorem
.then(Mono.fromCallable(() -> { // if there wasn't errors, mutate and save fooBar
fooBar.setActive(true);
return fooBar;
}).flatMap(fooBar1 -> saveFooBar(fooBar1))));
如果您可以使用 true active
标志创建 fooBar
的副本,代码可能会更简单。例如龙目岛。
@Builder(toBuilder = true)
public class FooBar {
...
}
Mono<FooBar> fooBarMono = findFooBar()
.flatMap(fooBar -> Flux.merge(
createAndSaveLoremBar(fooBar),
createAndSaveDoloremBar(fooBar))
.then(saveFooBar(fooBar.toBuilder().active(true).build())));
如果您对 saveFooBar(...)
的结果不感兴趣,而只对完成信号感兴趣,则可以异步进行所有三个保存:
Flux<Object> flux = findFooBar()
.flatMapMany(fooBar -> Flux.merge(
createAndSaveLoremBar(fooBar),
createAndSaveDoloremBar(fooBar),
saveFooBar(fooBar.toBuilder().active(true).build())));
实际上,在最后一种方法中,您可以收集所有三个结果,您应该更喜欢这种方法,但我没有足够的信息来了解您的 类 以及创建完整示例的要求。
我认为下面的解决方案更具可读性。无论如何亚历山大是正确的,你永远不应该修改输入。你看我们借用了很多概念形式的函数式编程。例如你调用 Function.identity()
这个叫做
identity functor
。 Flux
和 Mono
都是单子。有一个对这些人来说是秘密的概念,叫做 Referential transparency
命令更新中断。
final Mono<FooBar> fooBarMono1 = findFooBar()
.zipWhen((fooBar) -> createAndSaveLoremBar(fooBar))
.map(tuple -> tuple.getT1())
.zipWhen((fooBar) -> createAndSaveDoloremBar(fooBar))
.map(tuple -> tuple.getT1())
.map(fooBar -> new FooBar(true))
.flatMap(fooBar -> saveFooBar(fooBar));
或者更简洁:
final Mono<FooBar> fooBarMono1 = findFooBar()
.zipWhen((fooBar) -> createAndSaveLoremBar(fooBar)
.then(createAndSaveDoloremBar(fooBar)))
.map(tuple -> tuple.getT1())
.map(fooBar -> new FooBar(true))
.flatMap(fooBar -> saveFooBar(fooBar));