Skip to content

Instantly share code, notes, and snippets.

@lordmulder
Last active January 25, 2021 21:32
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 lordmulder/2c491c8a01d870c1e2bc1c5cab619158 to your computer and use it in GitHub Desktop.
Save lordmulder/2c491c8a01d870c1e2bc1c5cab619158 to your computer and use it in GitHub Desktop.
AsyncTask for JavaFX/OpenJFX
/*
* AsyncTask for JavaFX/OpenJFX, by LoRd_MuldeR <mulder2@gmx.de>
*
* This program is free software. It comes without any warranty, to
* the extent permitted by applicable law. You can redistribute it
* and/or modify it under the terms of the Do What The Fuck You Want
* To Public License, Version 2, as published by Sam Hocevar. See
* http://www.wtfpl.net/ for more details.
*/
import java.util.Objects;
import java.util.concurrent.Callable;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import javafx.application.Platform;
import javafx.concurrent.Task;
public class AsyncTask {
private static class ThreadPoolHolder {
private static final ExecutorService THREAD_POOL = Executors.newCachedThreadPool();
}
// ======================================================================
// Exception wrapper
// ======================================================================
@SuppressWarnings("serial")
private static class AsyncTaskError extends IllegalStateException {
public AsyncTaskError(final Throwable e) {
super(e);
}
}
// ======================================================================
// Task implementation
// ======================================================================
private static class AsyncTaskRunner<T> extends Task<T> {
private final Callable<T> callable;
public AsyncTaskRunner(final Callable<T> callable) {
this.callable = Objects.requireNonNull(callable, "Callable must not be null!");
}
@Override
protected final T call() throws Exception {
return Objects.requireNonNull(callable.call(), "Async task result must not be null!");
}
@Override
protected final void succeeded() {
Platform.exitNestedEventLoop(this, getValue());
}
@Override
protected final void cancelled() {
Platform.exitNestedEventLoop(this, new AsyncTaskError(new CancellationException("Async task was cancelled before completion!")));
}
@Override
protected final void failed() {
Platform.exitNestedEventLoop(this, new AsyncTaskError(getException()));
}
};
// ======================================================================
// Public methods
// ======================================================================
public static <T> T await(final Callable<T> callable) throws Exception {
checkThread();
return await(new AsyncTaskRunner<T>(callable));
}
// ======================================================================
// Internal methods
// ======================================================================
@SuppressWarnings("unchecked")
private static <T> T await(final Task<T> task) throws Exception {
try {
ThreadPoolHolder.THREAD_POOL.submit(task);
} catch (final Exception e) {
throw new IllegalStateException("Failed to schedule the async task!", e);
}
final Object result = Platform.enterNestedEventLoop(task);
if (result instanceof AsyncTaskError) {
throwErrorResult((AsyncTaskError) result);
}
return (T) result;
}
private static void throwErrorResult(final AsyncTaskError error) throws Exception {
final Throwable cause = error.getCause();
if (cause instanceof Exception) {
throw (Exception) cause;
} else if (cause instanceof Error) {
throw (Error) cause;
} else {
throw new IllegalStateException("Async task has failed for an unknown reason!");
}
}
private static void checkThread() {
if (!Platform.isFxApplicationThread()) {
throw new IllegalStateException("This method must onyl be called from FX application thread!");
}
}
}
public class MyController {
@FXML
public void onButtonClicked() {
try {
final String result = AsyncTask.await(() -> {
return longRunningOperation());
});
doSomethingWithResult(result);
} catch(Exception e) {
/* handle exceptions */
}
}
/* ... */
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment