Reputation: 5628
I have some code that saves an entity to a database using spring-data
and then performs some other work foo()
and bar()
that needs the id
from the entity that has been saved. It looks like this:
private CompletableFuture<Void> save(MyEntity me) {
CompletableFuture<Void> future = ContextAwareCompletableFuture
.runAsync(() -> repository.save(me))
.thenRunAsync(() -> foo(me))
.thenRunAsync(() -> bar(me));
return future;
}
private Foo foo(MyEntitiy me) {
// Use the identifier for me to update some foo in another world
}
private Bar bar(MyEntitiy me) {
// Use the identifier for me to update some bar in at another time
}
Now, I do not want to return void
from my save
method. I want to return a MyEntity
so I tried:
private CompletableFuture<MyEntity> save(MyEntity me) {
CompletableFuture<MyEntity> future = ContextAwareCompletableFuture
.runAsync(() -> repository.save(me))
.thenRunAsync(() -> foo(me))
.thenRunAsync(() -> bar(me));
return future;
}
This does not work since runAsync
returns void. My method repository.save()
returns the object I wish to return but that call is at the beginning of the chain. I need to save the object before I can do my foo
and bar
.
So next thing I tried is:
private CompletableFuture<MyEntity> save(MyEntity me) {
CompletableFuture<MyEntity> future = ContextAwareCompletableFuture
.supplyAsync(() -> repository.save(me))
.thenApplyAsync((e) -> baz(e);
return future;
}
private MyEntity baz(MyEntitiy me) {
foo(me);
bar(me);
return me;
}
Now, that seems wrong to me. Foo
and Bar
will now have to be executed during the same stage and they might take some time.
How can I return the object saved in repository.save()
after foo
and bar
has finished properly?
Upvotes: 2
Views: 1276
Reputation: 8135
If foo
and bar
may run concurrently, you may choose to chain on save
instead of sequencing them:
private CompletableFuture<MyEntity> save(MyEntity me) {
CompletableFuture<MyEntity> future = ContextAwareCompletableFuture
.supplyAsync(() -> repository.save(me));
CompletableFuture<Void> fooFuture = future
.thenAcceptAsync((e) -> foo(e));
CompletableFuture<Void> barFuture = future
.thenAcceptAsync((e) -> bar(e));
return future
.thenCombine(fooFuture, (result, fooResult) -> result)
.thenCombine(barFuture, (result, barResult) -> result);
}
Note I used thenAcceptAsync
instead of thenRunAsync
to avoid capturing me
. I avoided the capture in the end as well.
We can avoid one thenCombine
if we return the entity on fooFuture
and barFuture
:
private CompletableFuture<MyEntity> save(MyEntity me) {
CompletableFuture<MyEntity> future = ContextAwareCompletableFuture
.supplyAsync(() -> repository.save(me));
CompletableFuture<MyEntity> fooFuture = future
.thenApplyAsync((e) -> { foo(e); return e; });
CompletableFuture<MyEntity> barFuture = future
.thenApplyAsync((e) -> { bar(e); return e; });
return fooFuture
.thenCombine(barFuture, (fooResult, barResult) -> fooResult);
}
Upvotes: 2
Reputation: 15714
You could chain with a method that does stuff and returns the input:
.thenApply(e -> { foo(e); return e; }
Upvotes: -1