Last active
January 25, 2021 21:32
-
-
Save lordmulder/2c491c8a01d870c1e2bc1c5cab619158 to your computer and use it in GitHub Desktop.
AsyncTask for JavaFX/OpenJFX
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* 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!"); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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