Partner – Microsoft – NPI (cat=Java)
announcement - icon

Microsoft JDConf 2024 conference is getting closer, on March 27th and 28th. Simply put, it's a free virtual event to learn about the newest developments in Java, Cloud, and AI.

Josh Long and Mark Heckler are kicking things off in the keynote, so it's definitely going to be both highly useful and quite practical.

This year’s theme is focused on developer productivity and how these technologies transform how we work, build, integrate, and modernize applications.

For the full conference agenda and speaker lineup, you can explore JDConf.com:

>> RSVP Now

1. Overview

JDeferred is a small Java library (also supports Groovy) used for implementing asynchronous topology without writing boilerplate code. This framework is inspired by the Jquery’s Promise/Ajax feature and Android’s Deferred Object pattern.

In this tutorial, we’ll show how to use JDeferred and its different utilities.

2. Maven Dependency

We can start using JDeferred in any application by adding the following dependency into our pom.xml:

<dependency>
    <groupId>org.jdeferred</groupId>
    <artifactId>jdeferred-core</artifactId>
    <version>1.2.6</version>
</dependency>

We can check the latest version of the JDeferred project in the Central Maven Repository.

3. Promises

Let’s have a look at a simple use-case of invoking an error-prone synchronous REST API call and perform some task based on the data returned by the API.

In simple JQuery, the above scenario can be addressed in the following way:

$.ajax("/GetEmployees")
    .done(
        function() {
            alert( "success" );
        }
     )
    .fail(
        function() {
            alert( "error" );
        }
     )
    .always(
        function() {
            alert( "complete" );
        }
    );

Similarly, JDeferred comes with the Promise and Deferred interfaces which register a thread-independent hook on the corresponding object that triggers different customizable action based on that object status.

Here, Deferred acts as the trigger and the Promise acts as the observer.

We can easily create this type of asynchronous workflow:

Deferred<String, String, String> deferred
  = new DeferredObject<>();
Promise<String, String, String> promise = deferred.promise();

promise.done(result -> System.out.println("Job done"))
  .fail(rejection -> System.out.println("Job fail"))
  .progress(progress -> System.out.println("Job is in progress"))
  .always((state, result, rejection) -> 
    System.out.println("Job execution started"));

deferred.resolve("msg");
deferred.notify("notice");
deferred.reject("oops");

Here, each method has different semantics:

  • done() – triggers only when the pending actions on the deferred object is/are completed successfully
  • fail() – triggers while some exception is raised while performing pending action/s on the deferred object
  • progress() – triggers as soon as pending actions on the deferred object is/are started to execute
  • always() – triggers regardless of deferred object’s state

By default, a deferred object’s status can be PENDING/REJECTED/RESOLVED. We can check the status using deferred.state() method.

Point to note here is that once a deferred object’s status is changed to RESOLVED, we can’t perform reject operation on that object.

Similarly, once the object’s status is changed to REJECTED, we can’t perform resolve or notify operation on that object. Any violation will result into an IllegalStateExeption.

4. Filters

Before retrieving the final result, we can perform filtering on the deferred object with DoneFilter.

Once the filtering is done, we’ll get the thread-safe deferred object:

private static String modifiedMsg;

static String filter(String msg) {
    Deferred<String, ?, ?> d = new DeferredObject<>();
    Promise<String, ?, ?> p = d.promise();
    Promise<String, ?, ?> filtered = p.then((result) > {
        modifiedMsg = "Hello "  result;
    });

    filtered.done(r > System.out.println("filtering done"));

    d.resolve(msg);
    return modifiedMsg;
}

5. Pipes

Similar to filter, JDeferred offers the DonePipe interface to perform sophisticated post-filtering actions once the deferred object pending actions are resolved.

public enum Result { 
    SUCCESS, FAILURE 
}; 

private static Result status; 

public static Result validate(int num) { 
    Deferred<Integer, ?, ?> d = new DeferredObject<>(); 
    Promise<Integer, ?, ?> p = d.promise(); 
    
    p.then((DonePipe<Integer, Integer, Exception, Void>) result > {
        public Deferred<Integer, Exception, Void> pipeDone(Integer result) {
            if (result < 90) {
                return new DeferredObject<Integer, Exception, Void>()
                  .resolve(result);
            } else {
                return new DeferredObject<Integer, Exception, Void>()
                  .reject(new Exception("Unacceptable value"));
            }
    }).done(r > status = Result.SUCCESS )
      .fail(r > status = Result.FAILURE );

    d.resolve(num);
    return status;
}

Here, based on the value of the actual result, we’ve raised an exception to reject the result.

6. Deferred Manager

In a real time scenario, we need to deal with the multiple deferred objects observed by multiple promises. In this scenario, it’s pretty difficult to manage multiple promises separately.

That’s why JDeferred comes with DeferredManager interface which creates a common observer for all of the promises. Hence, using this common observer, we can create common actions for all of the promises:

Deferred<String, String, String> deferred = new DeferredObject<>();
DeferredManager dm = new DefaultDeferredManager();
Promise<String, String, String> p1 = deferred.promise(), 
  p2 = deferred.promise(), 
  p3 = deferred.promise();
dm.when(p1, p2, p3)
  .done(result -> ... )
  .fail(result -> ... );
deferred.resolve("Hello Baeldung");

We can also assign ExecutorService with a custom thread pool to the DeferredManager:

ExecutorService executor = Executors.newFixedThreadPool(10);
DeferredManager dm = new DefaultDeferredManager(executor);

In fact, we can completely ignore the use of Promise and can directly define the Callable interface to complete the task:

DeferredManager dm = new DefaultDeferredManager();
dm.when(() -> {
    // return something and raise an exception to interrupt the task
}).done(result -> ... )
  .fail(e -> ... );

7. Thread-Safe Action

Although, most of the time we need to deal with asynchronous workflow, some of the time we need to wait for the results of the all of the parallel tasks.

In this type of scenario, we may only use Object‘s wait() method to wait for all deferred tasks to finish:

DeferredManager dm = new DefaultDeferredManager();
Deferred<String, String, String> deferred = new DeferredObject<>();
Promise<String, String, String> p1 = deferred.promise();
Promise<String, String, String> p = dm
  .when(p1)
  .done(result -> ... )
  .fail(result -> ... );

synchronized (p) {
    while (p.isPending()) {
        try {
            p.wait();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

deferred.resolve("Hello Baeldung");

Alternatively, we can use Promise interface’s waitSafely() method to achieve the same.

try {
    p.waitSafely();
} catch (InterruptedException e) {
    e.printStackTrace();
}

Although both of the above methods perform pretty much the same thing, it’s always advisable to use the second one since the second procedure doesn’t require synchronization.

8. Android Integration

JDeferred can be easily integrated with Android applications using the Android Maven plugin.

For APKLIB build, we need to add the following dependency in the pom.xml:

<dependency>
    <groupId>org.jdeferred</groupId>
    <artifactId>jdeferred-android</artifactId>
    <version>1.2.6</version>
    <type>apklib</type>
</dependency>

For AAR build, we need to add the following dependency in the pom.xml:

<dependency>
    <groupId>org.jdeferred</groupId>
    <artifactId>jdeferred-android-aar</artifactId>
    <version>1.2.6</version>
    <type>aar</type>
</dependency>

9. Conclusion

In this tutorial, we explored about JDeferred, and it’s different utilities.

As always, the full source code is available over on GitHub.

Course – LS (cat=Java)

Get started with Spring and Spring Boot, through the Learn Spring course:

>> CHECK OUT THE COURSE
res – REST with Spring (eBook) (everywhere)
Comments are closed on this article!