简体   繁体   中英

Chaining in CompletableFuture to return first value

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?

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

您可以使用一个可以填充并返回输入的方法进行链接:

.thenApply(e -> { foo(e); return e; }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM