Home > Software engineering >  Return object from async method into CompletableFuture<Object> list?
Return object from async method into CompletableFuture<Object> list?

Time:01-30

I have a list of objects (from model class Name) that I want to process using an async method. The async method will take each Name object in the list and process it (check if it's not taken), I want to have a list of Names after they all get processed by the async method to get returned to the isNameAvailable(LinkedHashSet<Name> nameList) method

My executor config

@Configuration
@ManagedResource
public class ThreadConfig {
    @Bean(name = "nameAvailabilityExecutor")
    public Executor nameAvailabilityExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(100);
        executor.setMaxPoolSize(1200);
        executor.setQueueCapacity(10000);
        executor.setThreadNamePrefix("nameAvailabilityExecutor-");
        executor.initialize();
        return executor;
    }
}

Service class which will call the Async method in another class

public LinkedHashSet<Name> isNameAvailable(LinkedHashSet<Name> nameList) {
    LinkedHashSet<Name> nameCheckedList = new LinkedHashSet<>();

    for (Name nameObj : nameList) {
        Name nameCheckedObj = domainAvailabilityServiceThread.isNameAvailable(nameObj);
        nameCheckedList.add(nameCheckedObj);
    }

    return nameCheckedList;
}

The async method which will do the processing

@Async("nameAvailabilityExecutor")
public Name isNameAvailable(Name nameObj) {
    String name = nameObj.getName();

    if (getByNameCheck(name)) {
        nameObj.setAvailable(true);
    } else {
        nameObj.setAvailable(false);
    }

    return nameObj;
}

From my understanding CompletableFuture is what I need to use here? What is the correct way using CompletableFuture in this scenario?

CodePudding user response:

@Async can be used for fire-and-forget scenarios, such as sending an email, kicking off a database job, some long-running background task. Caller immediately gets the response, while a background job continues processing.

So it's not a good choice for your requirements since you want to return come result after completion.

Well, CompletableFuture API would be a good choice for you. You can combine them, merge results and etc, putting the callbacks to them.

CodePudding user response:

You can simply make isNameAvailable(Name) return a CompletableFuture:

@Async("nameAvailabilityExecutor")
public CompletableFuture<Name> isNameAvailable(Name nameObj) {
    String name = nameObj.getName();

    if (getByNameCheck(name)) {
        nameObj.setAvailable(true);
    } else {
        nameObj.setAvailable(false);
    }

    return CompletableFuture.completedFuture(nameObj);
}

Spring @Async will deal with the asynchronous execution as you intended.

You will also need to change the return type of isNameAvailable(LinkedHashSet) to a simple List or something similar, since it does not really make sense to store CompletableFutures in a Set:

public List<CompletableFuture<Name>> isNameAvailable(LinkedHashSet<Name> nameList) {
    List<CompletableFuture<Name>> nameCheckedList = new ArrayList<>();

    for (Name nameObj : nameList) {
        CompletableFuture<Name> nameCheckedObj = domainAvailabilityServiceThread.isNameAvailable(nameObj);
        nameCheckedList.add(nameCheckedObj);
    }

    return nameCheckedList;
}

Note that it is probably not a good idea to asynchronously modify the state of an object like you are doing here with Name, as it makes it more difficult to guarantee what state will be visible to the calling thread. It might be preferable to work with CompletableFuture<Boolean>:

@Async("nameAvailabilityExecutor")
public CompletableFuture<Boolean> isNameAvailable(Name nameObj) {
    String name = nameObj.getName();

    return CompletableFuture.completedFuture(getByNameCheck(name));
}

and return a Map<Name, CompletableFuture<Boolean>>:

public Map<Name, CompletableFuture<Boolean>> isNameAvailable(LinkedHashSet<Name> nameList) {
    Map<Name, CompletableFuture<Boolean>> nameCheckedList = new HashMap<>();

    for (Name nameObj : nameList) {
        CompletableFuture<Boolean>> nameCheckedObj = domainAvailabilityServiceThread.isNameAvailable(nameObj);
        nameCheckedList.put(nameObj, nameCheckedObj);
    }

    return nameCheckedList;
}

and let the calling thread do whatever is needed with that check.

  • Related