org.reactfx.Await Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of reactfx Show documentation
Show all versions of reactfx Show documentation
Reactive event streams for JavaFX
package org.reactfx;
import static javafx.concurrent.WorkerStateEvent.*;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.Executor;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import javafx.beans.binding.BooleanBinding;
import javafx.beans.value.ObservableBooleanValue;
import javafx.concurrent.Task;
import org.reactfx.util.TriConsumer;
import org.reactfx.util.Try;
class Await extends EventStreamBase> implements AwaitingEventStream> {
public static AwaitingEventStream> awaitCompletionStage(
EventStream> source,
Executor clientThreadExecutor) {
return new Await<>(
source,
(future, handler) -> addCompletionHandler(future, handler, clientThreadExecutor));
}
public static AwaitingEventStream> awaitTask(
EventStream> source) {
return new Await<>(source, Await::addCompletionHandler);
}
static void addCompletionHandler(
CompletionStage future,
TriConsumer handler,
Executor executor) {
future.whenCompleteAsync((result, error) -> {
handler.accept(result, error, false);
}, executor);
}
static void addCompletionHandler(
Task t,
TriConsumer handler) {
t.addEventHandler(WORKER_STATE_SUCCEEDED, e -> handler.accept(t.getValue(), null, false));
t.addEventHandler(WORKER_STATE_FAILED, e -> handler.accept(null, t.getException(), false));
t.addEventHandler(WORKER_STATE_CANCELLED, e -> handler.accept(null, null, true));
}
private final EventStream source;
private final Indicator pending = new Indicator();
private final BiConsumer> addCompletionHandler;
private Await(
EventStream source,
BiConsumer> addCompletionHandler) {
this.source = source;
this.addCompletionHandler = addCompletionHandler;
}
@Override
public final ObservableBooleanValue pendingProperty() {
return pending;
}
@Override
public final boolean isPending() {
return pending.isOn();
}
@Override
protected final Subscription observeInputs() {
return source.subscribe(future -> {
Guard g = pending.on();
addCompletionHandler.accept(future, (result, error, cancelled) -> {
if(!cancelled) {
emit(error == null ? Try.success(result) : Try.failure(error));
}
g.close();
});
});
}
}
class AwaitLatest extends EventStreamBase> implements AwaitingEventStream> {
public static AwaitingEventStream> awaitCompletionStage(
EventStream> source,
Executor clientThreadExecutor) {
return new AwaitLatest<>(
source,
EventStreams.never(), // no cancel impulse
future -> {}, // cannot cancel a CompletionStage
(future, handler) -> Await.addCompletionHandler(future, handler, clientThreadExecutor));
}
public static AwaitingEventStream> awaitTask(
EventStream> source) {
return new AwaitLatest<>(
source,
EventStreams.never(), // no cancel impulse
Task::cancel,
Await::addCompletionHandler);
}
public static AwaitingEventStream> awaitCompletionStage(
EventStream> source,
EventStream> cancelImpulse,
Executor clientThreadExecutor) {
return new AwaitLatest<>(
source,
cancelImpulse,
future -> {}, // cannot cancel a CompletionStage
(future, handler) -> Await.addCompletionHandler(future, handler, clientThreadExecutor));
}
public static AwaitingEventStream> awaitTask(
EventStream> source,
EventStream> cancelImpulse) {
return new AwaitLatest<>(
source,
cancelImpulse,
Task::cancel,
Await::addCompletionHandler);
}
private final EventStream source;
private final EventStream> cancelImpulse;
private final Consumer canceller;
private final BiConsumer> addCompletionHandler;
private long revision = 0;
private F expectedFuture = null;
private BooleanBinding pending = null;
private AwaitLatest(
EventStream source,
EventStream> cancelImpulse,
Consumer canceller,
BiConsumer> addCompletionHandler) {
this.source = source;
this.cancelImpulse = cancelImpulse;
this.canceller = canceller;
this.addCompletionHandler = addCompletionHandler;
}
@Override
public ObservableBooleanValue pendingProperty() {
if(pending == null) {
pending = new BooleanBinding() {
@Override
protected boolean computeValue() {
return expectedFuture != null;
}
};
}
return pending;
}
@Override
public boolean isPending() {
return pending != null ? pending.get() : expectedFuture != null;
}
@Override
protected Subscription observeInputs() {
Subscription s1 = source.subscribe(future -> {
long rev = replaceExpected(future);
addCompletionHandler.accept(future, (result, error, cancelled) -> {
if(rev == revision) {
if(!cancelled) {
// emit before setting pending to false
emit(error == null ? Try.success(result) : Try.failure(error));
}
setExpected(null);
}
});
});
Subscription s2 = cancelImpulse.subscribe(x -> replaceExpected(null));
return s1.and(s2);
}
private final long replaceExpected(F newExpected) {
++revision; // increment before cancelling, so that the cancellation handler is not executed
if(expectedFuture != null) {
canceller.accept(expectedFuture);
}
setExpected(newExpected);
return revision;
}
private void setExpected(F newExpected) {
expectedFuture = newExpected;
if(pending != null) {
pending.invalidate();
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy