#Java's CompletableFuture Understanding

1 messages · Page 1 of 1 (latest)

drowsy ginkgo
#

So I have been looking into Java's CompletableFuture as a requirement for performance work in our code base.

I understand the concept of how to use it to run things asynchronously.

I am having issues trying to implement it in a way that returns me a flat list, e.g.

List<T> myList = CompletableFuture.supplyAsync(...);

I can get the result if I just simply return a CompletableFuture List: CompletableFuture<List<T>> myList = ...

And then I can convert it to a List by using the .get() method provided by CompletableFuture, but this looks messy to me:

...
CompletableFuture<List<T>> myFuturesList = CompletableFuture.supplyAsync(() -> getData());

List<T> myList = myFuturesList.get();
...

Part of the issue I have is I want to call a method that returns a list of Integers, that I then pass into my method to retrieve an Object of AccountDetails.

So basically I do something like:

Get customer -> getAccountIds(customer) -> getAccountDetails(accountId)

return List<accountDetails> 
pallid flowerBOT
#

<@&987246399047479336> please have a look, thanks.

pallid flowerBOT
#

While you are waiting for getting help, here are some tips to improve your experience:

Code is much easier to read if posted with syntax highlighting and proper formatting.

If nobody is calling back, that usually means that your question was not well asked and hence nobody feels confident enough answering. Try to use your time to elaborate, provide details, context, more code, examples and maybe some screenshots. With enough info, someone knows the answer for sure.

Don't forget to close your thread using the command </help-thread close:1027500463647621170> when your question has been answered, thanks.

sterile bloom
#

whats the point of using futures/async if u immediately block and wait for the result

#

defeats the idea

drowsy ginkgo
drowsy ginkgo
sterile bloom
#

ur not supposed to return a list

proven verge
#

You should use .thenAccept so you can gain the performance async givesnyou

sterile bloom
#

return a future

#

and the user can then use .thenAccept or .thenApply to continue the chain of operations

#

asynchronously

proven verge
#

Make an instance of CompletableFuture, and the use thenAccept and/or thenApply, usually those are non-blocking proper ways to get values of stuff without it blocking

#

That's how you properly access a value in CompletableFutures

drowsy ginkgo
#

Okay what I have, (this is all dummy code with constant and test data in separate classes):

Customer customer = Customers.getCustomerFromId(1);
List<Integer> accountIds = GetCustomerAccounts.getAccountIdsFromCustomerId(customer);

CompletableFuture<List<AccountDetails>> accountDetailsFutures = 
        CompletableFuture.supplyAsync(() -> accountIds.stream()
                .map(GetAccountDetails::getDetails)
                .collect(Collectors.toList()));
pallid flowerBOT
drowsy ginkgo
#

Is there a better way to do it?

sterile bloom
#

i dont really see the point of using futures here

#

where is the long call that u want to avoid?

#

what is it that u want to execute in parallel?

#

what ur doing here looks completely pointless

#

and/or incorrect

drowsy ginkgo
#

Okay so the idea is this is practice as in production we would make multiple calls to retrieve data using a single response that is a Collection.
So what I am trying to execute in parallel in threads is the GetAccountDetails.getDetails(accountId).
The idea here is, a customer who has multiple accounts, we retrieve the id of the account, then we retrieve the details of each individual account using the id.

sterile bloom
#

what is getDetails doing?

#

is it a long method?

#

does it do rest calls?

#

db lookups?

#

what does it do

drowsy ginkgo
#

getDetails in this instance doesn't do that. When it comes to production, it will be doing lookups to dbs. But for my practice case it returns account details that are stored in their own class, but would be in a db in reality.

sterile bloom
#

so bc it will be looking into the DB, u want to do it async or whats the motivation here?

#

if its not actually taking long, u will be making things slower by introducing futures/async/... here

drowsy ginkgo
#

Yeah the idea is we do a lookup and before, we would just iterate through the initial result, so instead of sequentially doing a db lookup, we spin up multiple threads to make n number of calls to the same lookup using information retrieved in a previous call.

sterile bloom
#

i dont quite get what ur explaining here but anyways

#

so u want to make all ur getDetails() calls in parallel, okay

#

but now, do u want to do sth else while this is doing its thing

#

or do u want to wait for all the details to return as a list and then do sth with it

#

i.e. do u just want to do all of them in parallel or additionally also do sth else while its doing that

drowsy ginkgo
#

Okay for this example I am pretending I am hooking into a db. In reality I would have 1 thread spinning multiple threads for n Accounts for example, then a second thread would be retrieving different data from a different db lookup, then we wait for both to complete and return Future X.

In my practice, I am just trying to replicate doing db lookups by calling getDetails(). So right now I am only executing 1 method for multiple customers asynchronously. Eventually this will tie in further

sterile bloom
#

can u please just answer my question

#

if its unclear, i can give an example:

drowsy ginkgo
#

I am trying, I am just confused and my brain isn't working so I might be misunderstanding

sterile bloom
#

if u want to bake a cake. instead of sending one person to buy the ingredients, u can send 10 people. they will finish faster, cause they find the stuff quicker.

now, do u want to wait until they return with all the ingredients, to then bake the cake, or do u want to do sth else in the meantime, like watching TV?

drowsy ginkgo
#

Okay for this practice, right now, I want to send all the calls off at once, wait for all of them to finish, then log the results, (I would return the result in prod but for practice I just want to log the information).

sterile bloom
#

then future is the wrong tool for u

#

future is when u want to do sth else in the meantime

#

which u dont

#

u want to wait for all the account details to arrive

#

and then work with the result

#

all ur interested in is making sure u request the account details all at once and not sequentially

#

so all u need is a parallel stream

#
List<AccountDetails> details = accountIds.parallelStream()
  .map(GetAccountDetails::getDetails)
  .toList();
#

thats all u need

drowsy ginkgo
#

Okay, now if I then needed to do something else, so while I am waiting for those details to come back, let's say I also want to retrieve a list of data stored elsewhere, wait for the getDetails lookup and the other lookup, I would then need futures, correct?

How would I achieve that?

sterile bloom
#

if u want to do sth else while its doing its finding all the details in parallel, u would wrap futures around it

#

lets say:

#
var futureDetails = CompletableFuture.supplyAsync(() -> accountIds.parallelStream()
  .map(GetAccountDetails::getDetails)
  toList()); // details are triggered, they are computing now

doSomethingElse(); // this is running while details compute
// ... still while details compute

// Okay, lets now wait for them to arrive
var details = futureDetails.join();
// use them
#

or alternatively continue decoupled:

#
var futureDetails = CompletableFuture.supplyAsync(() -> accountIds.parallelStream()
  .map(GetAccountDetails::getDetails)
  toList()); // details are triggered, they are computing now

// ...
// once details are done, also do ...
futureDetails.thenAccept(details -> doSomethingWithThem(details));
#

that way u can essentially put more into the "pipeline"

#

it all depends on what u want to achieve

drowsy ginkgo
#

Right I think I have it understood. Went through it in a call with my team lead and I think I have a better handle of what's going on and the differences of each supply, run, apply, accept, combine, compose. I think I need to sus out CompletableFuture.allOf() as it looks like the work I need to implement relies on waiting for 2 threads to complete, then returning the results from both threads

#

Or would .join() method allow this result too? Basically the method wants to create 2 streams of work, one looking up one thing, the other looking up another, then we wait for both streams of work to complete, both streams running asynchronously to do db lookups, then return the results once both streams have completed

zenith wren
#

again, the question is whether you want to do something else while those queries are running

#

maybe you only need to launch one future and do the other one in the current thread