DEV Community

riversun
riversun

Posted on • Updated on

How to use Promise in Java

I've developed a promise library for java so let me introduce it.

Overview

Quick Look

Writing a Promise in Javascript

A typical example of using promise in JavaScript is:

Promise.resolve('foo')
    .then(function (data) {
        return new Promise(function (resolve, reject) {
            setTimeout(function () {
                const newData = data + 'bar';
                resolve(newData);
            }, 1);
        });
    })
    .then(function (data) {
        return new Promise(function (resolve, reject) {
            console.log(data);
        });
    });
console.log("Promise in JavaScript");
Enter fullscreen mode Exit fullscreen mode

Writing a Promise in java-promise

Write the same thing using java-promise

import org.riversun.promise.Promise;
public class Example {

    public static void main(String[] args) {
        Promise.resolve("foo")
                .then(new Promise((action, data) -> {
                    new Thread(() -> {
                        String newData = data + "bar";
                        action.resolve(newData);
                    }).start();
                }))
                .then(new Promise((action, data) -> {
                    System.out.println(data);
                    action.resolve();
                }))
                .start();
        System.out.println("Promise in Java");
    }
}
Enter fullscreen mode Exit fullscreen mode

Syntax:

Yes,you can write in a syntax similar to JavaScript as follows:

Promise.resolve()
        .then(new Promise(funcFulfilled1), new Promise(funcRejected1))
        .then(new Promise(functionFulfilled2), new Promise(functionRejected2))
        .start();
Enter fullscreen mode Exit fullscreen mode

Dependency

Maven

<dependency>
    <groupId>org.riversun</groupId>
    <artifactId>java-promise</artifactId>
    <version>1.1.0</version>
</dependency>
Enter fullscreen mode Exit fullscreen mode

Gradle

compile group: 'org.riversun', name: 'java-promise', version: '1.1.0'
Enter fullscreen mode Exit fullscreen mode

Quick Start

Execute sequentially by chained "then"

  • Use Promise.then() to chain operations.
  • Write your logic in Func.run(action,data).
  • Start operation by Promise.start and run asynchronously(run on worker thread)
  • Calling action.resolve makes the promise fullfilled state and passes the result to the next then
public class Example00 {

    public static void main(String[] args) {

        Func function1 = (action, data) -> {
            new Thread(() -> {
                System.out.println("Process-1");
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {}
                //Specify result value.(Any type can be specified)
                action.resolve("Result-1");
            }).start();
        };

        Func function2 = (action, data) -> {
            System.out.println("Process-2 result=" + data);
            action.resolve();
        };

        Promise.resolve()
                .then(new Promise(function1))
                .then(new Promise(function2))
                .start();// start Promise operation

        System.out.println("Hello,Promise");
    }
}
Enter fullscreen mode Exit fullscreen mode

Diagram:

Result:

Hello,Promise
Process-1
Process-1 result=Result-1
Enter fullscreen mode Exit fullscreen mode

Tips

It's also OK to just write Promise.then(func).

Promise.resolve()
        .then(function1)
        .then(function2)
        .start();// start Promise operation
Enter fullscreen mode Exit fullscreen mode

Description

What is "Func" ?

Func is a java interface equivalent to JavaScript's Function for argument of #then

public interface Func {
    public void run(Action action, Object data) throws Exception;
}
Enter fullscreen mode Exit fullscreen mode

You can write Func like a JavaScript function.

I want to show two ways of implementing Func class.

No.1)Write Func object in the normal way.

Func function = new Func() {
    @Override
    public void run(Action action, Object data) throws Exception {
        System.out.println("Process");//write your logic
        action.resolve();
    }
};
Enter fullscreen mode Exit fullscreen mode

No.2)Write Func object using lambda expression.

Func function = (action, data) -> {
    System.out.println("Process");//write your logic
    action.resolve();
};
Enter fullscreen mode Exit fullscreen mode

What is "Action" ?

Action object is an argument of Func#run method.

  • Call action.resolve( [fulfillment value] ) to make the Promise's status fulfilled and move on to the next processing(specified by then) with the result(fulfillment value).
action.resolve("Success");
Enter fullscreen mode Exit fullscreen mode
  • Call action.reject( [rejection reason] ) to make the Promise's status rejected and move on to the next processing(specified by then) with the result(rejection reason).
action.reject("Failure");
Enter fullscreen mode Exit fullscreen mode
  • Argument is optional, you can call action.resolve() or action.reject()
action.resolve();//Argument can be omitted
Enter fullscreen mode Exit fullscreen mode

Usage

Rejection

If action.reject() is called, or if an exception thrown while executing Func.run(), rejected status is set to Promise, and the onRejected function specified to then is called.

  • call action.reject
Func function = (action, data) -> {
  action.reject("Failure");
};
Enter fullscreen mode Exit fullscreen mode
  • throw an exception
Func function = (action, data) -> {
  throw new Exception("something");
};

Enter fullscreen mode Exit fullscreen mode

Let's see Promise.then() method,

the 2nd argument of Promise.then() can be set to a Func to receive the result of rejection when receiving the result of then.

  • Syntax

    Usage Promise.then(onFulfilled[, onRejected]);

  • onFulfilled is a Func object called if the Promise is fulfilled.

    You can receive the previous execution "fulfilled" result as an argument named data.

  • onRejected is a Func object called if the Promise is rejected.

    You can receive the previous execution "rejected" result(mainly the objects are exceptions) as an argument named data.

//Rejection
public class ExampleRejection {
    public static void main(String[] args) {
        Promise.resolve()
                .then((action, data) -> {
                    System.out.println("Process-1");
                    action.reject();
                })
                .then(
                        // call when resolved
                        (action, data) -> {
                            System.out.println("Resolved Process-2");
                            action.resolve();
                        },
                        // call when rejected
                        (action, data) -> {
                            System.out.println("Rejected Process-2");
                            action.resolve();
                        })
                .start();// start Promise operation

        System.out.println("Hello,Promise");
    }
}
Enter fullscreen mode Exit fullscreen mode

Diagram:

Result:

Hello,Promise
Process-1
Rejected Process-2
Enter fullscreen mode Exit fullscreen mode

Promise.always

Promise.always() always receive both fulfilled and rejected results.

public class ExampleAlways {

    public static void main(String[] args) {
        Func func2OutReject = (action, data) -> {
            action.reject("I send REJECT");
            //action.resolve("I send RESOLVE");
        };
        Func func2ReceiveAlways = (action, data) -> {
            System.out.println("Received:" + data);
            action.resolve();
        };
        Promise.resolve()
                .then(func2OutReject)
                .always(func2ReceiveAlways)
                .start();
    }
}
Enter fullscreen mode Exit fullscreen mode

Diagram:

Result

Received:I send REJECT
Enter fullscreen mode Exit fullscreen mode

Promise.all

Execute multiple promises at the same time, and after all executions are complete, move to the next processing with then

  • Execute multiple promises simultaneously and wait until all the execution is finished before proceeding.
  • If all finishes with resolve, execution results will be stored as java.util.List in the order of invocation.
  • If there is even one rejection, store that rejection reason in the result when the rejection occurs and move on to the next "then".
import java.util.List;

import org.riversun.promise.Func;
import org.riversun.promise.Promise;

public class ExampleAll {
    public static void main(String[] args) {
        Func func1 = (action, data) -> {
            Promise.sleep(1000);
            System.out.println("func1 running");
            action.resolve("func1-result");
        };
        Func func2 = (action, data) -> {
            Promise.sleep(500);
            System.out.println("func2 running");
            action.resolve("func2-result");
        };
        Func func3 = (action, data) -> {
            Promise.sleep(1500);
            System.out.println("func3 running");
            action.resolve("func3-result");
        };
        Func funcGetResult = (action, data) -> {
            List<Object> resultList = (List<Object>) data;
            for (int i = 0; i < resultList.size(); i++) {
                Object o = resultList.get(i);
                System.out.println("No." + (i + 1) + " result is " + o);
            }
            action.resolve();
        };
        Promise.all(func1, func2, func3)
                .always(funcGetResult)
                .start();
    }
}
Enter fullscreen mode Exit fullscreen mode

Diagram:

Result:

func2 running
func1 running
func3 running
No.1 result is func1-result
No.2 result is func2-result
No.3 result is func3-result
Enter fullscreen mode Exit fullscreen mode

Threading

It is also possible to execute Promise processing on the specified executor.
Note if you use your own executor, remember to shut it down after use.
If you use your own executor, it will NOT be shutdown automatically  

At least one worker thread to be used in Promise.all,
and one thread for overall asynchronous execution, so a total of two or more threads must be needed.

public class Example {

    public static void main(String[] args) {

        final ExecutorService myExecutor = Executors.newFixedThreadPool(5);

        Func func1 = (action, data) -> {
            System.out.println("func1 on " + Thread.currentThread().getName());
            action.resolve();
        };

        Func func2 = (action, data) -> {
            System.out.println("func2 on " + Thread.currentThread().getName());
            action.resolve();
        };

        Func func3 = (action, data) -> {
            System.out.println("func3 on " + Thread.currentThread().getName());
            action.resolve();
        };

        Promise.all(myExecutor, func1, func2, func3)
                .then((action, data) -> {
                    System.out.println("final process on " + Thread.currentThread().getName());
                    myExecutor.shutdown();//If you use your own executor, remember to shut it down after use
                    action.resolve();
                })
                .start();
    }
}
Enter fullscreen mode Exit fullscreen mode

Result:

func1 on pool-1-thread-2
func2 on pool-1-thread-3
func3 on pool-1-thread-4
final process on pool-1-thread-1
Enter fullscreen mode Exit fullscreen mode

SyncPromise

SyncPromise, as the name implies, is a synchronous promise.

While Promise is executed asynchronously, SyncPromise does NOT move next while it is chained by "then".

All other features are the same as Promise.

public class Example02 {

    public static void main(String[] args) {
        Func func1 = (action, data) -> {
            new Thread(() -> {
                System.out.println("Process-1");
                action.resolve();
            }).start();

        };
        Func func2 = (action, data) -> {
            new Thread(() -> {
                System.out.println("Process-2");
                action.resolve();
            }).start();

        };
        SyncPromise.resolve()
                .then(func1)
                .then(func2)
                .start();
        System.out.println("Hello,Promise");
    }
}
Enter fullscreen mode Exit fullscreen mode

Result:

Process-1
Process-2
Hello,Promise
Enter fullscreen mode Exit fullscreen mode

Even if func1 and func2 are executed in a thread,

System.out.println("Hello,Promise") is always executed after that.Because SyncPromise is synchronous execution.

Discussion (3)

Collapse
nickhristov profile image
Nick Hristov

Why? Use CompletableFuture.

Collapse
belenot profile image
belenot

Is there a typo in Quick start section's result's output from First example(last line: Result2 should be) ? Or maybe I didn't realize correctly?)

Collapse
riversun profile image
riversun Author

Thanks for your comment.
First example's output is as stated.In this example, I wanted to show that the results in the first "then" can be received by the next "then".
Additional questions are welcome.