Search code examples
javaasynchronousconcurrencyjava.util.concurrent

Wait for asynchronous callback to finish and pass the value


I have the following code snippet:

private ValContainer<CompletableFuture<String>> valContainer = new ValContainer<>();

public void fetchAsync(final String attrName) {

        QueryAsyncCallback asyncCallback = new QueryAsyncCallback() {
            @Override
            public void onSuccess(Message response) {
                final Data value = response.results().get("value");
                String text = value.toString();
                CompletableFuture<String> result = CompletableFuture.completedFuture(text);
                valContainer.setVal(result);
            }

            @Override
            public void onError(CallbackError error) {
                LOG.info("Error occurred");
            }

            @Override
            public void onTimeout() {
                LOG.info("Timeout occurred");
            }
        };

        Message request = new Messsage("com.example.val", "local", 0, "fetch");
        request.parameters().put("name", attrName);

        MsgOptions options = new MsgOptions(5000);
        try {
            queue.query(request, options, asyncCallback);
        } catch (QueueAccessException e) {
            e.printStackTrace();
        }
    }

Right now, I can easily get the values I made a query for in onSuccess() callback. ValContainer is just a generic container so that I can save the values inside anonymous class. Unfortunately, in Main function I am trying to access this valContainer but sadly as this is asynchronous query I am getting NullPointerException.

...
((JavaServiceImpl) javaService).fetchAsync("test");
System.out.println("inMain value: " + ((JavaServiceImpl) javaService).getValContainer().getVal().get()); <-- this is going to be NullPointerException
...

Then after 100ms I am getting the value in onSuccess() method. Here basically doing Thread.sleep(100) would be sufficient to acquire the value I am hoping to get but I really do not want to block the thread. Is there a way to do it in a neat way?


Solution

  • Don't leave the val un-initialized in ValContainer. Initialize it as new CompletableFuture<>().

    private ValContainer<CompletableFuture<String>> valContainer = new ValContainer<>(new CompletableFuture<>());
    
    
    public class ValContainer<T> {
        T val;
    
        public ValContainer(T val) {
            this.val = val;
        }
    }
    

    Then change your onSuccess method as

    String text = value.toString(); 
    valContainer.getVal().complete(text);
    

    You don't need to create a CompletableFuture in onSuccess method.

    With this, [..]getValContainer().getVal().get() blocks till the future is complete.