1. Introduction

In concurrent programming, asynchronous callbacks are self-evident, and in business development, asynchronous threads must be used if blocking tasks need to be executed. And, if we want to perform some action based on its result after asynchronous execution.

Futures prior to JDK 8 solved only half the problem of the above requirement, that is, executing asynchronously, returning a Future that requires the programmer to call the GET method to wait, or polling using isDone.

Not very efficient.

The new CompletableFuture API in JDK 8 solves this problem. But his API, to be honest, doesn’t work very well.

We just want a simple API that can implement our callback functionality.

I need 3 functions:

  1. Can return results via methods like GET.
  2. Can set listeners for callbacks.
  3. Success or failure can be set in the business thread.

Write a simple example, using Netty’s asynchronous API, hope to play a role.

Design of 2.

According to our requirements: First, we need a class with a get method and an addListener method. Second, we need a class that calls back the listeners we set up. Third, we need a class that can set success or failure in the business thread.

3. Preliminary implementation

Design a listener interface:

/** * listener *@author stateis0 
 */
public interface MyListener {
  /** * Subclasses need to override this method, which will be called back after the asynchronous task completes. *@paramPromise Asynchronous result placeholder. * /
  void operationComplete(MyPromise promise);
}
Copy the code

Design an asynchronous placeholder like Future:

/** * asynchronous execution result placeholder **@author stateis0
 */
public class MyPromise {

  /** listener collection */
  List<MyListener> listeners = new ArrayList<MyListener>();

  /** Successful */
  boolean success;

  /** Result **/
  Object result;

  /** Set the event counter **/
  int failCount;

  /** * Successfully set and notify all listeners. *@paramResult the results *@returnSuccessful */
  public boolean setSuccess(Object result) {
    if (success) {
      return false;
    }

    success = true;
    this.result = result;

    signalListeners();
    return true;
  }

  /** * notify all listeners and call back listener methods. * /
  private void signalListeners(a) {
    for (MyListener l : listeners) {
      l.operationComplete(this); }}/** * Failed to set *@paramE Abnormal object *@returnWhether the setting succeeded */
  public boolean setFail(Exception e) {
    if (failCount > 0) {
      return false;
    }
    ++failCount;
    result = e;
    signalListeners();
    return true;
  }

  /** * Indicates whether */ is successfully executed
  public boolean isSuccess(a) {
    return success;
  }

  /** * Add listener *@paramMyListener listener */
  public void addListener(MyListener myListener) {
    listeners.add(myListener);
  }

  /** * delete listener *@paramMyListener listener */
  public void removeListener(MyListener myListener) {
    listeners.remove(myListener);
  }

  /** * Obtain the result */
  public Object get(a) {
    returnresult; }}Copy the code

We want to use thread pools to perform this kind of task, so we need a custom Runnable, and within this Runnable we need to do some simple tricks:

/** * a task class that performs tasks by overriding the doWork method *@param<V> Return value type *@author stateis0 
 */
public abstract class MyRunnable<V> implements Runnable {

  final MyPromise myPromise;

  protected MyRunnable(MyPromise myPromise) {
    this.myPromise = myPromise;
  }

  @Override
  public void run(a) {
    try {
      V v = doWork();
      myPromise.setSuccess(v);
    } catch(Exception e) { myPromise.setFail(e); }}/** * subclasses need to override this method. And returns the value returned by the Promise's get method. * /
  public abstract V doWork(a);
}
Copy the code

4. Write a test Demo

/ * * *@author stateis0
 */
public class MyDemo {

  public static void main(String[] args) {

    // placeholder object
    final MyPromise myPromise = new MyPromise();

    final Dto dto = new Dto();

    / / thread pool
    Executor executor = Executors.newFixedThreadPool(1);

    // Execute tasks asynchronously,
    executor.execute(new MyRunnable<String>(myPromise) {
      @Override
      public String doWork(a) {
        returndto.doSomething(); }});// Add a listener
    myPromise.addListener(new MyListener() {
      // Execute this method when the task is complete.
      @Override
      public void operationComplete(MyPromise promise) {
        // Get the result
        String result;
        // If the task is successfully executed
        if (promise.isSuccess()) {
          // Get the result and print it
          result = (String) promise.get();
          System.out.println("operationComplete ----> " + result);
        }
        // If it fails, print the exception stack
        else{ ((Exception) promise.get()).printStackTrace(); }}}); }}class Dto {

  public String doSomething(a) {
    System.out.println("doSomething");
// throw new RuntimeException("cxs");
    return "result is success"; }}Copy the code

Execution Result:

doSomething
operationComplete ----> result is success
Copy the code

In line with our expectations. We want to call back the listener’s operationComplete method after the business object Dto’s doSomething returns successfully. If it fails, print the exception stack.

Of course, the overall code is relatively simple, just a primer.

In fact, if we pass a business object directly to a Callable or Runnable, when the Call or run methods are finished, we can execute our business object’s methods based on the results. This is the simplest and most direct asynchronous callback.

It’s just too coupled.

Asynchronous tasks are coupled to business tasks, and you can’t add multiple listeners or use Promise’s setSuccess and setFail capabilities, which allow for greater flexibility in setting success or failure in business threads.

For asynchrony, we can look at Netty API design, easy to understand and easy to use.