javaspringspring-bootasynchronousspring-async

Can I return an API response without waiting on other external API calls in Spring Boot?


Is this the correct way to use @Async in Spring Boot?

@Service
class someServiceImpl {
...
  public someResponseDTO getUsers(int userId) {
   // Do some logic
   ...
   // Call external API with another service method from another service impl
   anotherService.emailUserInTheBackground(userId);
   return someResponseDTO;
  }
...
}
@Service
public class AnotherService {
  @Async
  public void emailUserInTheBackground(int userId) {
    // This might take a while...
    ...
  }
}

Since emailUserInTheBackground() has @Async annotation and void return type, does it block the line return someResponseDTO at all?

All I wanted is to return the response to the caller without waiting because emailUserInTheBackground() takes too long to complete and isn't directly tied to the response object.


Solution

  • Yes that is the correct way to run a task in the background, you can mimick the thread blocking behavior by introducing a delay.

    @SpringBootApplication
    @EnableAsync
    public class MyApplication {
    
        public static void main(String[] arg) {
           SpringApplication.run(MyApplication.class);
        }
    }
    

    then you need to mark the emailUserInTheBackground method with @Async annotation.

    @Service
    class AnotherService {
    
        @Async
        public void emailUserInTheBackground(int userId) {
           try {
              TimeUnit.SECONDS.sleep(10);
              System.out.println("Print from async: "+ Thread.currentThread().getName());
           } catch (InterruptedException e) {
              e.printStackTrace();
           }
        }
    }
    

    Now add one more logger after a method call, you'll see getUsers(...) call completing first in a different thread even though the emailService thread is blocked for 10 seconds.

    anotherService.emailUserInTheBackground(userId);
    System.out.println("Print from service: "+ Thread.currentThread().getName());
    

    you can also use CompletableFuture to run a task in the background.

     public someResponseDTO getUsers(int userId) {
       // some other task
       ...
       // Call external API with another service method from another service impl
       CompletableFuture.runAsync(() -> anotherService.emailUserInTheBackground(userId)) 
       return someResponseDTO;
      }