Rename then-series functions

This commit is contained in:
huanghongxun 2019-02-24 10:47:44 +08:00
parent 34890f21b3
commit ed66b016db
20 changed files with 275 additions and 168 deletions

View File

@ -141,7 +141,7 @@ public final class LauncherHelper {
}
})
.then(Task.of(Schedulers.javafx(), () -> emitStatus(LoadingState.LOGGING_IN)))
.thenTaskResult(() -> Task.ofResult(i18n("account.methods"), () -> {
.thenCompose(() -> Task.ofResult(i18n("account.methods"), () -> {
try {
return account.logIn();
} catch (CredentialExpiredException e) {
@ -152,11 +152,11 @@ public final class LauncherHelper {
return account.playOffline().orElseThrow(() -> e);
}
}))
.thenResult(Schedulers.javafx(), authInfo -> {
.thenApply(Schedulers.javafx(), authInfo -> {
emitStatus(LoadingState.LAUNCHING);
return authInfo;
})
.thenResult(authInfo -> new HMCLGameLauncher(
.thenApply(authInfo -> new HMCLGameLauncher(
repository,
selectedVersion,
authInfo,
@ -165,7 +165,7 @@ public final class LauncherHelper {
? null // Unnecessary to start listening to game process output when close launcher immediately after game launched.
: new HMCLProcessListener(authInfo, setting, gameVersion.isPresent())
))
.thenTaskResult(launcher -> { // launcher is prev task's result
.thenCompose(launcher -> { // launcher is prev task's result
if (scriptFile == null) {
return new LaunchTask<>(launcher::launch).setName(i18n("version.launch"));
} else {
@ -175,7 +175,7 @@ public final class LauncherHelper {
}).setName(i18n("version.launch_script"));
}
})
.thenVoid(process -> { // process is LaunchTask's result
.thenAccept(process -> { // process is LaunchTask's result
if (scriptFile == null) {
PROCESSES.add(process);
if (launcherVisibility == LauncherVisibility.CLOSE)

View File

@ -110,13 +110,13 @@ public final class ModpackHelper {
if (modpack.getManifest() instanceof CurseManifest)
return new CurseInstallTask(profile.getDependency(), zipFile, modpack, ((CurseManifest) modpack.getManifest()), name)
.finalized(Schedulers.defaultScheduler(), success, failure);
.whenComplete(Schedulers.defaultScheduler(), success, failure);
else if (modpack.getManifest() instanceof HMCLModpackManifest)
return new HMCLModpackInstallTask(profile, zipFile, modpack, name)
.finalized(Schedulers.defaultScheduler(), success, failure);
.whenComplete(Schedulers.defaultScheduler(), success, failure);
else if (modpack.getManifest() instanceof MultiMCInstanceConfiguration)
return new MultiMCModpackInstallTask(profile.getDependency(), zipFile, modpack, ((MultiMCInstanceConfiguration) modpack.getManifest()), name)
.finalized(Schedulers.defaultScheduler(), success, failure)
.whenComplete(Schedulers.defaultScheduler(), success, failure)
.then(new MultiMCInstallVersionSettingTask(profile, ((MultiMCInstanceConfiguration) modpack.getManifest()), name));
else throw new IllegalStateException("Unrecognized modpack: " + modpack);
}

View File

@ -25,7 +25,6 @@ import org.jackhuang.hmcl.event.EventBus;
import org.jackhuang.hmcl.event.RefreshedVersionsEvent;
import org.jackhuang.hmcl.game.HMCLGameRepository;
import org.jackhuang.hmcl.game.ModpackHelper;
import org.jackhuang.hmcl.mod.Modpack;
import org.jackhuang.hmcl.setting.Accounts;
import org.jackhuang.hmcl.setting.Profile;
import org.jackhuang.hmcl.setting.Profiles;
@ -120,8 +119,8 @@ public final class LeftPaneController extends AdvancedListBox {
File modpackFile = new File("modpack.zip").getAbsoluteFile();
if (modpackFile.exists()) {
Task.ofResult(() -> CompressingUtils.findSuitableEncoding(modpackFile.toPath()))
.thenResult(encoding -> ModpackHelper.readModpackManifest(modpackFile.toPath(), encoding))
.thenVoid(modpack -> {
.thenApply(encoding -> ModpackHelper.readModpackManifest(modpackFile.toPath(), encoding))
.thenAccept(modpack -> {
AtomicReference<Region> region = new AtomicReference<>();
TaskExecutor executor = ModpackHelper.getInstallTask(repository.getProfile(), modpackFile, modpack.getName(), modpack)
.with(Task.of(Schedulers.javafx(), this::checkAccount)).executor();

View File

@ -62,7 +62,7 @@ public class AccountLoginPane extends StackPane {
progressBar.setVisible(true);
lblCreationWarning.setText("");
Task.ofResult(() -> oldAccount.logInWithPassword(password))
.finalized(Schedulers.javafx(), authInfo -> {
.whenComplete(Schedulers.javafx(), authInfo -> {
success.accept(authInfo);
fireEvent(new DialogCloseEvent());
progressBar.setVisible(false);

View File

@ -196,7 +196,7 @@ public class AddAccountPane extends StackPane {
Object additionalData = getAuthAdditionalData();
Task.ofResult(() -> factory.create(new Selector(), username, password, additionalData))
.finalized(Schedulers.javafx(), account -> {
.whenComplete(Schedulers.javafx(), account -> {
int oldIndex = Accounts.getAccounts().indexOf(account);
if (oldIndex == -1) {
Accounts.getAccounts().add(account);

View File

@ -106,7 +106,7 @@ public class AddAuthlibInjectorServerPane extends StackPane implements DialogAwa
Task.of(() -> {
serverBeingAdded = AuthlibInjectorServer.locateServer(url);
}).finalized(Schedulers.javafx(), (isDependentsSucceeded, exception) -> {
}).whenComplete(Schedulers.javafx(), (isDependentsSucceeded, exception) -> {
addServerPane.setDisable(false);
nextPane.hideSpinner();

View File

@ -93,13 +93,13 @@ public final class InstallerWizardProvider implements WizardProvider {
TaskResult<Version> ret = Task.ofResult(() -> version);
if (settings.containsKey("forge"))
ret = ret.thenTaskResult(profile.getDependency().installLibraryAsync((RemoteVersion) settings.get("forge")));
ret = ret.thenCompose(profile.getDependency().installLibraryAsync((RemoteVersion) settings.get("forge")));
if (settings.containsKey("liteloader"))
ret = ret.thenTaskResult(profile.getDependency().installLibraryAsync((RemoteVersion) settings.get("liteloader")));
ret = ret.thenCompose(profile.getDependency().installLibraryAsync((RemoteVersion) settings.get("liteloader")));
if (settings.containsKey("optifine"))
ret = ret.thenTaskResult(profile.getDependency().installLibraryAsync((RemoteVersion) settings.get("optifine")));
ret = ret.thenCompose(profile.getDependency().installLibraryAsync((RemoteVersion) settings.get("optifine")));
return ret.then(profile.getRepository().refreshVersionsAsync());
}

View File

@ -110,8 +110,8 @@ public final class ModpackPage extends StackPane implements WizardPage {
spinnerPane.showSpinner();
Task.ofResult(() -> CompressingUtils.findSuitableEncoding(selectedFile.toPath()))
.thenResult(encoding -> manifest = ModpackHelper.readModpackManifest(selectedFile.toPath(), encoding))
.finalized(Schedulers.javafx(), manifest -> {
.thenApply(encoding -> manifest = ModpackHelper.readModpackManifest(selectedFile.toPath(), encoding))
.whenComplete(Schedulers.javafx(), manifest -> {
spinnerPane.hideSpinner();
controller.getSettings().put(MODPACK_MANIFEST, manifest);
lblName.setText(manifest.getName());

View File

@ -59,7 +59,7 @@ public final class VanillaInstallWizardProvider implements WizardProvider {
if (settings.containsKey("optifine"))
builder.version((RemoteVersion) settings.get("optifine"));
return builder.buildAsync().finalized((a, b) -> profile.getRepository().refreshVersions())
return builder.buildAsync().whenComplete((a, b) -> profile.getRepository().refreshVersions())
.then(Task.of(Schedulers.javafx(), () -> profile.setSelectedVersion(name)));
}

View File

@ -128,7 +128,7 @@ public final class VersionsPage extends BorderPane implements WizardPage, Refres
@Override
public void refresh() {
transitionHandler.setContent(spinner, ContainerAnimations.FADE.getAnimationProducer());
executor = versionList.refreshAsync(gameVersion, downloadProvider).finalized((isDependentsSucceeded, exception) -> {
executor = versionList.refreshAsync(gameVersion, downloadProvider).whenComplete((isDependentsSucceeded, exception) -> {
if (isDependentsSucceeded) {
List<VersionsPageItem> items = loadVersions();

View File

@ -91,7 +91,7 @@ public final class ModListPage extends Control {
modManager.refreshMods();
return new LinkedList<>(modManager.getMods());
}
}).finalized(Schedulers.javafx(), (list, isDependentsSucceeded, exception) -> {
}).whenComplete(Schedulers.javafx(), (list, isDependentsSucceeded, exception) -> {
loadingProperty().set(false);
if (isDependentsSucceeded)
FXUtils.onWeakChangeAndOperate(parentTab.getSelectionModel().selectedItemProperty(), newValue -> {

View File

@ -118,7 +118,7 @@ public final class VersionSettingsPage extends StackPane implements DecoratorPag
FXUtils.smoothScrolling(scroll);
Task.ofResult(JavaVersion::getJavas).thenVoid(Schedulers.javafx(), list -> {
Task.ofResult(JavaVersion::getJavas).thenAccept(Schedulers.javafx(), list -> {
javaItem.loadChildren(list.stream()
.map(javaVersion -> javaItem.createChildren(javaVersion.getVersion() + i18n("settings.game.java_directory.bit",
javaVersion.getPlatform().getBit()), javaVersion.getBinary().toString(), javaVersion))
@ -274,7 +274,7 @@ public final class VersionSettingsPage extends StackPane implements DecoratorPag
if (versionSetting == null)
return;
Task.ofResult(versionSetting::getJavaVersion)
.thenVoid(Schedulers.javafx(), javaVersion -> javaItem.setSubtitle(Optional.ofNullable(javaVersion)
.thenAccept(Schedulers.javafx(), javaVersion -> javaItem.setSubtitle(Optional.ofNullable(javaVersion)
.map(JavaVersion::getBinary).map(Path::toString).orElse("Invalid Java Path")))
.start();
}

View File

@ -51,7 +51,7 @@ public class WorldListPage extends ListPage<WorldListItem> {
setLoading(true);
Task.ofResult(() -> World.getWorlds(savesDir).parallel().collect(Collectors.toList()))
.finalized(Schedulers.javafx(), (result, isDependentsSucceeded, exception) -> {
.whenComplete(Schedulers.javafx(), (result, isDependentsSucceeded, exception) -> {
setLoading(false);
if (isDependentsSucceeded)
itemsProperty().setAll(result.stream().map(WorldListItem::new).collect(Collectors.toList()));
@ -73,10 +73,10 @@ public class WorldListPage extends ListPage<WorldListItem> {
// Only accept one world file because user is required to confirm the new world name
// Or too many input dialogs are popped.
Task.ofResult(() -> new World(zipFile.toPath()))
.finalized(Schedulers.javafx(), world -> {
.whenComplete(Schedulers.javafx(), world -> {
Controllers.inputDialog(i18n("world.name.enter"), (name, resolve, reject) -> {
Task.of(() -> world.install(savesDir, name))
.finalized(Schedulers.javafx(), () -> {
.whenComplete(Schedulers.javafx(), () -> {
itemsProperty().add(new WorldListItem(new World(savesDir.resolve(name))));
resolve.run();
}, e -> {

View File

@ -91,7 +91,7 @@ public class DefaultDependencyManager extends AbstractDependencyManager {
public TaskResult<Version> installLibraryAsync(String gameVersion, Version version, String libraryId, String libraryVersion) {
VersionList<?> versionList = getVersionList(libraryId);
return versionList.loadAsync(gameVersion, getDownloadProvider())
.thenTaskResult(() -> installLibraryAsync(version, versionList.getVersion(gameVersion, libraryVersion)
.thenCompose(() -> installLibraryAsync(version, versionList.getVersion(gameVersion, libraryVersion)
.orElseThrow(() -> new IllegalStateException("Remote library " + libraryId + " has no version " + libraryVersion))));
}
@ -107,9 +107,9 @@ public class DefaultDependencyManager extends AbstractDependencyManager {
else
throw new IllegalArgumentException("Remote library " + libraryVersion + " is unrecognized.");
return task
.thenTaskResult(LibrariesUniqueTask::new)
.thenTaskResult(MaintainTask::new)
.thenTaskResult(newVersion -> new VersionJsonSaveTask(repository, newVersion));
.thenCompose(LibrariesUniqueTask::new)
.thenCompose(MaintainTask::new)
.thenCompose(newVersion -> new VersionJsonSaveTask(repository, newVersion));
}

View File

@ -22,7 +22,6 @@ import org.jackhuang.hmcl.game.Version;
import org.jackhuang.hmcl.task.ParallelTask;
import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.task.TaskResult;
import org.jackhuang.hmcl.util.AutoTypingMap;
import org.jackhuang.hmcl.util.function.ExceptionalFunction;
import org.jackhuang.hmcl.util.gson.JsonUtils;
@ -50,7 +49,7 @@ public class DefaultGameBuilder extends GameBuilder {
@Override
public Task buildAsync() {
return new VersionJsonDownloadTask(gameVersion, dependencyManager).thenTaskResult(rawJson -> {
return new VersionJsonDownloadTask(gameVersion, dependencyManager).thenCompose(rawJson -> {
Version original = JsonUtils.GSON.fromJson(rawJson, Version.class);
Version version = original.setId(name).setJar(null);
Task vanillaTask = downloadGameAsync(gameVersion, version).then(new ParallelTask(
@ -58,20 +57,20 @@ public class DefaultGameBuilder extends GameBuilder {
new GameLibrariesTask(dependencyManager, version) // Game libraries will be downloaded for multiple times partly, this time is for vanilla libraries.
).with(new VersionJsonSaveTask(dependencyManager.getGameRepository(), version))); // using [with] because download failure here are tolerant.
TaskResult<Version> libraryTask = vanillaTask.thenResult(() -> version);
TaskResult<Version> libraryTask = vanillaTask.thenSupply(() -> version);
if (toolVersions.containsKey("forge"))
libraryTask = libraryTask.thenTaskResult(libraryTaskHelper(gameVersion, "forge"));
libraryTask = libraryTask.thenCompose(libraryTaskHelper(gameVersion, "forge"));
if (toolVersions.containsKey("liteloader"))
libraryTask = libraryTask.thenTaskResult(libraryTaskHelper(gameVersion, "liteloader"));
libraryTask = libraryTask.thenCompose(libraryTaskHelper(gameVersion, "liteloader"));
if (toolVersions.containsKey("optifine"))
libraryTask = libraryTask.thenTaskResult(libraryTaskHelper(gameVersion, "optifine"));
libraryTask = libraryTask.thenCompose(libraryTaskHelper(gameVersion, "optifine"));
for (RemoteVersion remoteVersion : remoteVersions)
libraryTask = libraryTask.thenTaskResult(dependencyManager.installLibraryAsync(remoteVersion));
libraryTask = libraryTask.thenCompose(dependencyManager.installLibraryAsync(remoteVersion));
return libraryTask;
}).finalized((isDependentsSucceeded, exception) -> {
}).whenComplete((isDependentsSucceeded, exception) -> {
if (!isDependentsSucceeded)
dependencyManager.getGameRepository().getVersionRoot(name).delete();
});

View File

@ -156,7 +156,7 @@ public final class MultiMCModpackInstallTask extends Task {
}
}
dependencies.add(new MaintainTask(version).thenTaskResult(maintainedVersion -> new VersionJsonSaveTask(repository, maintainedVersion)));
dependencies.add(new MaintainTask(version).thenCompose(maintainedVersion -> new VersionJsonSaveTask(repository, maintainedVersion)));
dependencies.add(new MinecraftInstanceTask<>(zipFile, modpack.getEncoding(), "/" + manifest.getName() + "/minecraft", manifest, MODPACK_TYPE, repository.getModpackConfiguration(name)));
}

View File

@ -1,71 +0,0 @@
/*
* Hello Minecraft! Launcher
* Copyright (C) 2019 huangyuhui <huanghongxun2008@126.com> and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package org.jackhuang.hmcl.task;
import java.util.Collection;
import java.util.Collections;
/**
* A task that combines two tasks and make sure [pred] runs before succ.
*
* @author huangyuhui
*/
final class FinalizedTask extends Task {
private final Task pred;
private final FinalizedCallback callback;
private final Scheduler scheduler;
/**
* A task that combines two tasks and make sure pred runs before succ.
*
* @param pred the task that runs before succ.
* @param callback a callback that returns the task runs after pred, succ will be executed asynchronously. You can do something that relies on the result of pred.
*/
public FinalizedTask(Task pred, Scheduler scheduler, FinalizedCallback callback) {
this.pred = pred;
this.scheduler = scheduler;
this.callback = callback;
setSignificance(TaskSignificance.MODERATE);
}
@Override
public Scheduler getScheduler() {
return scheduler;
}
@Override
public void execute() throws Exception {
callback.execute(isDependentsSucceeded(), pred.getLastException());
if (!isDependentsSucceeded())
throw new SilentException();
}
@Override
public Collection<Task> getDependents() {
return Collections.singleton(pred);
}
@Override
public boolean isRelyingOnDependents() {
return false;
}
}

View File

@ -265,18 +265,6 @@ public abstract class Task {
return executor().test();
}
public final void subscribe(Task subscriber) {
new TaskExecutor(then(subscriber)).start();
}
public final void subscribe(Scheduler scheduler, ExceptionalRunnable<?> closure) {
subscribe(of(scheduler, closure));
}
public final void subscribe(ExceptionalRunnable<?> closure) {
subscribe(of(closure));
}
public final Task then(Task b) {
return then(convert(b));
}
@ -285,13 +273,29 @@ public abstract class Task {
return new CoupleTask(this, b, true);
}
public final <R> TaskResult<R> thenResult(Callable<R> supplier) {
return thenTaskResult(() -> Task.ofResult(supplier));
/**
* Returns a new TaskResult that, when this task completes
* normally, is executed using the default Scheduler.
*
* @param fn the function to use to compute the value of the returned TaskResult
* @param <U> the function's return type
* @return the new TaskResult
*/
public final <U> TaskResult<U> thenSupply(Callable<U> fn) {
return thenCompose(() -> Task.ofResult(fn));
}
public final <R> TaskResult<R> thenTaskResult(ExceptionalSupplier<TaskResult<R>, ?> taskSupplier) {
return new TaskResult<R>() {
TaskResult<R> then;
/**
* Returns a new TaskResult that, when this task completes
* normally, is executed.
*
* @param fn the function returning a new TaskResult
* @param <U> the type of the returned TaskResult's result
* @return the TaskResult
*/
public final <U> TaskResult<U> thenCompose(ExceptionalSupplier<TaskResult<U>, ?> fn) {
return new TaskResult<U>() {
TaskResult<U> then;
@Override
public Collection<? extends Task> getDependents() {
@ -300,7 +304,7 @@ public abstract class Task {
@Override
public void execute() throws Exception {
then = taskSupplier.get().storeTo(this::setResult);
then = fn.get().storeTo(this::setResult);
}
@Override
@ -318,17 +322,87 @@ public abstract class Task {
return new CoupleTask(this, b, false);
}
public final Task finalized(FinalizedCallback b) {
return finalized(Schedulers.defaultScheduler(), b);
/**
* Returns a new Task with the same exception as this task, that executes
* the given action when this task completes.
*
* <p>When this task is complete, the given action is invoked, a boolean
* value represents the execution status of this task, and the exception
* (or {@code null} if none) of this task as arguments. The returned task
* is completed when the action returns. If the supplied action itself
* encounters an exception, then the returned task exceptionally completes
* with this exception unless this task also completed exceptionally.
*
* @param action the action to perform
* @return the new Task
*/
public final Task whenComplete(FinalizedCallback action) {
return whenComplete(Schedulers.defaultScheduler(), action);
}
public final Task finalized(Scheduler scheduler, FinalizedCallback b) {
return new FinalizedTask(this, scheduler, b).setName(getCaller());
/**
* Returns a new Task with the same exception as this task, that executes
* the given action when this task completes.
*
* <p>When this task is complete, the given action is invoked, a boolean
* value represents the execution status of this task, and the exception
* (or {@code null} if none) of this task as arguments. The returned task
* is completed when the action returns. If the supplied action itself
* encounters an exception, then the returned task exceptionally completes
* with this exception unless this task also completed exceptionally.
*
* @param action the action to perform
* @param scheduler the executor to use for asynchronous execution
* @return the new Task
*/
public final Task whenComplete(Scheduler scheduler, FinalizedCallback action) {
return new Task() {
{
setSignificance(TaskSignificance.MODERATE);
}
@Override
public Scheduler getScheduler() {
return scheduler;
}
@Override
public void execute() throws Exception {
action.execute(isDependentsSucceeded(), Task.this.getLastException());
if (!isDependentsSucceeded())
throw new SilentException();
}
@Override
public Collection<Task> getDependents() {
return Collections.singleton(Task.this);
}
@Override
public boolean isRelyingOnDependents() {
return false;
}
}.setName(getCaller());
}
// T, K here is necessary, or javac cannot infer type of failure
public final <T extends Exception, K extends Exception> Task finalized(Scheduler scheduler, ExceptionalRunnable<T> success, ExceptionalConsumer<Exception, K> failure) {
return finalized(scheduler, (isDependentsSucceeded, exception) -> {
/**
* Returns a new Task with the same exception as this task, that executes
* the given actions when this task completes.
*
* <p>When this task is complete, the given success action is invoked, the
* given failure action is invoked with the exception of this task. The
* returned task is completed when the action returns. If the supplied
* action itself encounters an exception, then the returned task exceptionally
* completes with this exception unless this task also
* completed exceptionally.
*
* @param success the action to perform when this task successfully completed
* @param failure the action to perform when this task exceptionally returned
* @return the new Task
*/
public final <E1 extends Exception, E2 extends Exception> Task whenComplete(Scheduler scheduler, ExceptionalRunnable<E1> success, ExceptionalConsumer<Exception, E2> failure) {
return whenComplete(scheduler, (isDependentsSucceeded, exception) -> {
if (isDependentsSucceeded) {
if (success != null)
try {

View File

@ -133,6 +133,7 @@ public final class TaskExecutor {
private boolean executeTask(Task task) {
if (canceled) {
task.setState(Task.TaskState.FAILED);
task.setLastException(new CancellationException());
return false;
}
@ -221,7 +222,7 @@ public final class TaskExecutor {
task.onDone().fireEvent(new TaskEvent(this, task, true));
taskListeners.forEach(it -> it.onFailed(task, e));
} catch (SilentException | RejectedExecutionException e) {
// do nothing
task.setLastException(e);
} catch (Exception e) {
task.setLastException(e);
lastException = e;

View File

@ -29,35 +29,55 @@ import java.util.function.Consumer;
*
* @author huangyuhui
*/
public abstract class TaskResult<V> extends Task {
public abstract class TaskResult<T> extends Task {
private V result;
private Consumer<V> resultConsumer;
private T result;
private Consumer<T> resultConsumer;
@Override
public TaskResult<V> setName(String name) {
public TaskResult<T> setName(String name) {
super.setName(name);
return this;
}
public V getResult() {
/**
* Returns the result of this task.
*
* The result will be generated only if the execution is completed.
*/
public T getResult() {
return result;
}
public void setResult(V result) {
protected void setResult(T result) {
this.result = result;
if (resultConsumer != null)
resultConsumer.accept(result);
}
public TaskResult<V> storeTo(Consumer<V> resultConsumer) {
this.resultConsumer = resultConsumer;
/**
* Sync the result of this task by given action.
*
* @param action the action to perform when result of this task changed
* @return this TaskResult
*/
public TaskResult<T> storeTo(Consumer<T> action) {
this.resultConsumer = action;
return this;
}
public <R, E extends Exception> TaskResult<R> thenTaskResult(ExceptionalFunction<V, TaskResult<R>, E> taskSupplier) {
return new TaskResult<R>() {
TaskResult<R> then;
/**
* Returns a new TaskResult that, when this task completes
* normally, is executed with result of this task as the argument
* to the supplied function.
*
* @param fn the function returning a new TaskResult
* @param <U> the type of the returned TaskResult's result
* @return the TaskResult
*/
public <U, E extends Exception> TaskResult<U> thenCompose(ExceptionalFunction<T, TaskResult<U>, E> fn) {
return new TaskResult<U>() {
TaskResult<U> then;
@Override
public Collection<? extends Task> getDependents() {
@ -66,7 +86,7 @@ public abstract class TaskResult<V> extends Task {
@Override
public void execute() throws Exception {
then = taskSupplier.apply(TaskResult.this.getResult()).storeTo(this::setResult);
then = fn.apply(TaskResult.this.getResult()).storeTo(this::setResult);
}
@Override
@ -76,44 +96,129 @@ public abstract class TaskResult<V> extends Task {
};
}
public <R, E extends Exception> Task then(ExceptionalFunction<V, Task, E> taskSupplier) {
return new CoupleTask(this, () -> taskSupplier.apply(getResult()), true);
/**
* Returns a new Task that, when this task completes
* normally, is executed with this task as the argument
* to the supplied function.
*
* @param fn the function returning a new Task
* @return the Task
*/
public <E extends Exception> Task then(ExceptionalFunction<T, Task, E> fn) {
return new CoupleTask(this, () -> fn.apply(getResult()), true);
}
public <R, E extends Exception> TaskResult<R> thenResult(ExceptionalFunction<V, R, E> task) {
return thenResult(Schedulers.defaultScheduler(), task);
/**
* Returns a new TaskResult that, when this task completes
* normally, is executed using the default Scheduler, with this
* task's result as the argument to the supplied function.
*
* @param fn the function to use to compute the value of the returned TaskResult
* @param <U> the function's return type
* @return the new TaskResult
*/
public <U, E extends Exception> TaskResult<U> thenApply(ExceptionalFunction<T, U, E> fn) {
return thenApply(Schedulers.defaultScheduler(), fn);
}
public <R, E extends Exception> TaskResult<R> thenResult(Scheduler scheduler, ExceptionalFunction<V, R, E> task) {
return thenResult(getCaller(), scheduler, task);
/**
* Returns a new TaskResult that, when this task completes
* normally, is executed using the supplied Scheduler, with this
* task's result as the argument to the supplied function.
*
* @param scheduler the executor to use for asynchronous execution
* @param fn the function to use to compute the value of the returned TaskResult
* @param <U> the function's return type
* @return the new TaskResult
*/
public <U, E extends Exception> TaskResult<U> thenApply(Scheduler scheduler, ExceptionalFunction<T, U, E> fn) {
return thenApply(getCaller(), scheduler, fn);
}
public <R, E extends Exception> TaskResult<R> thenResult(String name, Scheduler scheduler, ExceptionalFunction<V, R, E> task) {
return new Subtask<>(name, scheduler, task);
/**
* Returns a new TaskResult that, when this task completes
* normally, is executed using the supplied Scheduler, with this
* task's result as the argument to the supplied function.
*
* @param name the name of this new TaskResult for displaying
* @param scheduler the executor to use for asynchronous execution
* @param fn the function to use to compute the value of the returned TaskResult
* @param <U> the function's return type
* @return the new TaskResult
*/
public <U, E extends Exception> TaskResult<U> thenApply(String name, Scheduler scheduler, ExceptionalFunction<T, U, E> fn) {
return new Subtask<>(name, scheduler, fn);
}
// stupid javac stop us from renaming thenVoid to thenResult
public <E extends Exception> Task thenVoid(ExceptionalConsumer<V, E> task) {
return thenVoid(Schedulers.defaultScheduler(), task);
/**
* Returns a new Task that, when this task completes
* normally, is executed using the default Scheduler, with this
* task's result as the argument to the supplied action.
*
* @param action the action to perform before completing the
* returned Task
* @return the new Task
*/
public <E extends Exception> Task thenAccept(ExceptionalConsumer<T, E> action) {
return thenAccept(Schedulers.defaultScheduler(), action);
}
public <E extends Exception> Task thenVoid(Scheduler scheduler, ExceptionalConsumer<V, E> task) {
return new CoupleTask(this, () -> Task.of(scheduler, () -> task.accept(getResult())), true);
/**
* Returns a new Task that, when this task completes
* normally, is executed using the supplied Scheduler, with this
* task's result as the argument to the supplied action.
*
* @param action the action to perform before completing the returned Task
* @param scheduler the executor to use for asynchronous execution
* @return the new Task
*/
public <E extends Exception> Task thenAccept(Scheduler scheduler, ExceptionalConsumer<T, E> action) {
return new CoupleTask(this, () -> Task.of(scheduler, () -> action.accept(getResult())), true);
}
public <T extends Exception, K extends Exception> Task finalized(Scheduler scheduler, ExceptionalConsumer<V, T> success, ExceptionalConsumer<Exception, K> failure) {
return finalized(scheduler, () -> success.accept(getResult()), failure);
/**
* Returns a new Task with the same exception as this task, that executes
* the given actions when this task completes.
*
* <p>When this task is complete, the given success action is invoked with
* the result, the given failure action is invoked with the exception of
* this task. The returned task is completed when the action returns. If
* the supplied action itself encounters an exception, then the returned
* task exceptionally completes with this exception unless this task also
* completed exceptionally.
*
* @param success the action to perform when this task successfully completed
* @param failure the action to perform when this task exceptionally returned
* @return the new Task
*/
public <E1 extends Exception, E2 extends Exception> Task whenComplete(Scheduler scheduler, ExceptionalConsumer<T, E1> success, ExceptionalConsumer<Exception, E2> failure) {
return whenComplete(scheduler, () -> success.accept(getResult()), failure);
}
public Task finalized(Scheduler scheduler, FinalizedCallback<V> callback) {
return finalized(scheduler, ((isDependentsSucceeded, exception) -> callback.execute(getResult(), isDependentsSucceeded, exception)));
/**
* Returns a new Task with the same exception as this task, that executes
* the given action when this task completes.
*
* <p>When this task is complete, the given action is invoked with the
* result (or {@code null} if none), a boolean value represents the
* execution status of this task, and the exception (or {@code null}
* if none) of this task as arguments. The returned task is completed
* when the action returns. If the supplied action itself encounters an
* exception, then the returned task exceptionally completes with this
* exception unless this task also completed exceptionally.
*
* @param action the action to perform
* @return the new Task
*/
public Task whenComplete(Scheduler scheduler, FinalizedCallback<T> action) {
return whenComplete(scheduler, ((isDependentsSucceeded, exception) -> action.execute(getResult(), isDependentsSucceeded, exception)));
}
private class Subtask<R> extends TaskResult<R> {
private final Scheduler scheduler;
private final ExceptionalFunction<V, R, ?> callable;
private final ExceptionalFunction<T, R, ?> callable;
public Subtask(String name, Scheduler scheduler, ExceptionalFunction<V, R, ?> callable) {
public Subtask(String name, Scheduler scheduler, ExceptionalFunction<T, R, ?> callable) {
this.scheduler = scheduler;
this.callable = callable;