Skip to content

Instantly share code, notes, and snippets.

@loriopatrick
Created June 17, 2017 08:22
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save loriopatrick/d5eb4df08095fa820cfc4e7a3cc88d4f to your computer and use it in GitHub Desktop.
Save loriopatrick/d5eb4df08095fa820cfc4e7a3cc88d4f to your computer and use it in GitHub Desktop.
Promises in Java modeled after NodeJS
package com.patricklorio.promise;
import java.util.ArrayList;
import java.util.function.Consumer;
/**
* @author plorio
*/
public class Promise<T> {
public Promise(Async<T> async) {
try {
async.async(this::success, this::error);
} catch (Throwable throwable) {
error(throwable);
}
}
public static <K> Promise<K> resolve(K value) {
Promise<K> future = new Promise<>();
future.success = value;
future.state = 1;
return future;
}
public static Promise<Void> reject(Throwable error) {
Promise<Void> future = new Promise<>();
future.error = error;
future.state = 2;
return future;
}
private Promise() {
}
private final Object lock = new Object();
private int state = 0;
private T success = null;
private Throwable error = null;
private final ArrayList<Consumer<T>> successListeners = new ArrayList<>();
private final ArrayList<Consumer<Throwable>> errorListeners = new ArrayList<>();
private void success(T value) {
synchronized (lock) {
if (state != 0) {
throw new IllegalStateException();
}
state = 1;
success = value;
for (Consumer<T> listener : successListeners) {
listener.accept(value);
}
successListeners.clear();
}
}
private void error(Throwable value) {
synchronized (lock) {
if (state != 0) {
throw new IllegalStateException();
}
state = 2;
error = value;
for (Consumer<Throwable> listener : errorListeners) {
listener.accept(value);
}
errorListeners.clear();
}
}
private void onSuccess(Consumer<T> listener) {
synchronized (lock) {
if (state == 1) {
listener.accept(success);
return;
}
if (state == 0) {
successListeners.add(listener);
}
}
}
private void onError(Consumer<Throwable> listener) {
synchronized (lock) {
if (state == 2) {
listener.accept(error);
return;
}
if (state == 0) {
errorListeners.add(listener);
}
}
}
public <R> Promise<R> then(ValueHandler<T, R> handler) {
Promise<R> future = new Promise<>();
onSuccess(value -> {
final R handlerResult;
try {
handlerResult = handler.handle(value);
} catch (Throwable error) {
future.error(error);
return;
}
future.success(handlerResult);
});
onError(future::error);
return future;
}
public Promise<Void> then(Handler<T> handler) {
Promise<Void> future = new Promise<>();
onSuccess(value -> {
try {
handler.handle(value);
} catch (Throwable error) {
future.error(error);
return;
}
future.success(null);
});
onError(future::error);
return future;
}
public <R> Promise<R> compose(ChainHandler<T, R> handler) {
Promise<R> future = new Promise<>();
onSuccess(value -> {
final Promise<R> handlerResult;
try {
handlerResult = handler.handle(value);
} catch (Throwable error) {
future.error(error);
return;
}
handlerResult.onSuccess(future::success);
handlerResult.onError(future::error);
});
onError(future::error);
return future;
}
public <E extends Throwable> Promise<T> exception(Class<E> errorClass, ValueHandler<E, T> handler) {
return exception(error -> {
if (errorClass.isAssignableFrom(errorClass)) {
return handler.handle(errorClass.cast(error));
}
throw error;
});
}
public Promise<T> exception(ValueHandler<Throwable, T> handler) {
Promise<T> future = new Promise<>();
onError(error -> {
final T handlerResult;
try {
handlerResult = handler.handle(error);
} catch (Throwable error2) {
future.error(error2);
return;
}
future.success(handlerResult);
});
onSuccess(future::success);
return future;
}
public <E extends Throwable> Promise<T> exceptionCompose(Class<E> errorClass, ChainHandler<E, T> handler) {
return exceptionCompose(error -> {
if (errorClass.isAssignableFrom(errorClass)) {
return handler.handle(errorClass.cast(error));
}
throw error;
});
}
public Promise<T> exceptionCompose(ChainHandler<Throwable, T> handler) {
Promise<T> future = new Promise<>();
onError(error -> {
final Promise<T> handlerResult;
try {
handlerResult = handler.handle(error);
} catch (Throwable error2) {
future.error(error2);
return;
}
handlerResult.onSuccess(future::success);
handlerResult.onError(future::error);
});
onSuccess(future::success);
return future;
}
public interface Async<T> {
void async(Consumer<T> resolve, Consumer<Throwable> reject) throws Throwable;
}
public interface Handler<T> {
void handle(T value) throws Throwable;
}
public interface ChainHandler<T, R> {
Promise<R> handle(T value) throws Throwable;
}
public interface ValueHandler<T, R> {
R handle(T value) throws Throwable;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment