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

View File

@@ -110,13 +110,13 @@ public final class ModpackHelper {
if (modpack.getManifest() instanceof CurseManifest) if (modpack.getManifest() instanceof CurseManifest)
return new CurseInstallTask(profile.getDependency(), zipFile, modpack, ((CurseManifest) modpack.getManifest()), name) 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) else if (modpack.getManifest() instanceof HMCLModpackManifest)
return new HMCLModpackInstallTask(profile, zipFile, modpack, name) return new HMCLModpackInstallTask(profile, zipFile, modpack, name)
.finalized(Schedulers.defaultScheduler(), success, failure); .whenComplete(Schedulers.defaultScheduler(), success, failure);
else if (modpack.getManifest() instanceof MultiMCInstanceConfiguration) else if (modpack.getManifest() instanceof MultiMCInstanceConfiguration)
return new MultiMCModpackInstallTask(profile.getDependency(), zipFile, modpack, ((MultiMCInstanceConfiguration) modpack.getManifest()), name) 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)); .then(new MultiMCInstallVersionSettingTask(profile, ((MultiMCInstanceConfiguration) modpack.getManifest()), name));
else throw new IllegalStateException("Unrecognized modpack: " + modpack); 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.event.RefreshedVersionsEvent;
import org.jackhuang.hmcl.game.HMCLGameRepository; import org.jackhuang.hmcl.game.HMCLGameRepository;
import org.jackhuang.hmcl.game.ModpackHelper; import org.jackhuang.hmcl.game.ModpackHelper;
import org.jackhuang.hmcl.mod.Modpack;
import org.jackhuang.hmcl.setting.Accounts; import org.jackhuang.hmcl.setting.Accounts;
import org.jackhuang.hmcl.setting.Profile; import org.jackhuang.hmcl.setting.Profile;
import org.jackhuang.hmcl.setting.Profiles; import org.jackhuang.hmcl.setting.Profiles;
@@ -120,8 +119,8 @@ public final class LeftPaneController extends AdvancedListBox {
File modpackFile = new File("modpack.zip").getAbsoluteFile(); File modpackFile = new File("modpack.zip").getAbsoluteFile();
if (modpackFile.exists()) { if (modpackFile.exists()) {
Task.ofResult(() -> CompressingUtils.findSuitableEncoding(modpackFile.toPath())) Task.ofResult(() -> CompressingUtils.findSuitableEncoding(modpackFile.toPath()))
.thenResult(encoding -> ModpackHelper.readModpackManifest(modpackFile.toPath(), encoding)) .thenApply(encoding -> ModpackHelper.readModpackManifest(modpackFile.toPath(), encoding))
.thenVoid(modpack -> { .thenAccept(modpack -> {
AtomicReference<Region> region = new AtomicReference<>(); AtomicReference<Region> region = new AtomicReference<>();
TaskExecutor executor = ModpackHelper.getInstallTask(repository.getProfile(), modpackFile, modpack.getName(), modpack) TaskExecutor executor = ModpackHelper.getInstallTask(repository.getProfile(), modpackFile, modpack.getName(), modpack)
.with(Task.of(Schedulers.javafx(), this::checkAccount)).executor(); .with(Task.of(Schedulers.javafx(), this::checkAccount)).executor();

View File

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

View File

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

View File

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

View File

@@ -93,13 +93,13 @@ public final class InstallerWizardProvider implements WizardProvider {
TaskResult<Version> ret = Task.ofResult(() -> version); TaskResult<Version> ret = Task.ofResult(() -> version);
if (settings.containsKey("forge")) 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")) 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")) 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()); return ret.then(profile.getRepository().refreshVersionsAsync());
} }

View File

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

View File

@@ -59,7 +59,7 @@ public final class VanillaInstallWizardProvider implements WizardProvider {
if (settings.containsKey("optifine")) if (settings.containsKey("optifine"))
builder.version((RemoteVersion) settings.get("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))); .then(Task.of(Schedulers.javafx(), () -> profile.setSelectedVersion(name)));
} }

View File

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

View File

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

View File

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

View File

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

View File

@@ -91,7 +91,7 @@ public class DefaultDependencyManager extends AbstractDependencyManager {
public TaskResult<Version> installLibraryAsync(String gameVersion, Version version, String libraryId, String libraryVersion) { public TaskResult<Version> installLibraryAsync(String gameVersion, Version version, String libraryId, String libraryVersion) {
VersionList<?> versionList = getVersionList(libraryId); VersionList<?> versionList = getVersionList(libraryId);
return versionList.loadAsync(gameVersion, getDownloadProvider()) 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)))); .orElseThrow(() -> new IllegalStateException("Remote library " + libraryId + " has no version " + libraryVersion))));
} }
@@ -107,9 +107,9 @@ public class DefaultDependencyManager extends AbstractDependencyManager {
else else
throw new IllegalArgumentException("Remote library " + libraryVersion + " is unrecognized."); throw new IllegalArgumentException("Remote library " + libraryVersion + " is unrecognized.");
return task return task
.thenTaskResult(LibrariesUniqueTask::new) .thenCompose(LibrariesUniqueTask::new)
.thenTaskResult(MaintainTask::new) .thenCompose(MaintainTask::new)
.thenTaskResult(newVersion -> new VersionJsonSaveTask(repository, newVersion)); .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.ParallelTask;
import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.task.TaskResult; import org.jackhuang.hmcl.task.TaskResult;
import org.jackhuang.hmcl.util.AutoTypingMap;
import org.jackhuang.hmcl.util.function.ExceptionalFunction; import org.jackhuang.hmcl.util.function.ExceptionalFunction;
import org.jackhuang.hmcl.util.gson.JsonUtils; import org.jackhuang.hmcl.util.gson.JsonUtils;
@@ -50,7 +49,7 @@ public class DefaultGameBuilder extends GameBuilder {
@Override @Override
public Task buildAsync() { 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 original = JsonUtils.GSON.fromJson(rawJson, Version.class);
Version version = original.setId(name).setJar(null); Version version = original.setId(name).setJar(null);
Task vanillaTask = downloadGameAsync(gameVersion, version).then(new ParallelTask( 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. 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. ).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")) if (toolVersions.containsKey("forge"))
libraryTask = libraryTask.thenTaskResult(libraryTaskHelper(gameVersion, "forge")); libraryTask = libraryTask.thenCompose(libraryTaskHelper(gameVersion, "forge"));
if (toolVersions.containsKey("liteloader")) if (toolVersions.containsKey("liteloader"))
libraryTask = libraryTask.thenTaskResult(libraryTaskHelper(gameVersion, "liteloader")); libraryTask = libraryTask.thenCompose(libraryTaskHelper(gameVersion, "liteloader"));
if (toolVersions.containsKey("optifine")) if (toolVersions.containsKey("optifine"))
libraryTask = libraryTask.thenTaskResult(libraryTaskHelper(gameVersion, "optifine")); libraryTask = libraryTask.thenCompose(libraryTaskHelper(gameVersion, "optifine"));
for (RemoteVersion remoteVersion : remoteVersions) for (RemoteVersion remoteVersion : remoteVersions)
libraryTask = libraryTask.thenTaskResult(dependencyManager.installLibraryAsync(remoteVersion)); libraryTask = libraryTask.thenCompose(dependencyManager.installLibraryAsync(remoteVersion));
return libraryTask; return libraryTask;
}).finalized((isDependentsSucceeded, exception) -> { }).whenComplete((isDependentsSucceeded, exception) -> {
if (!isDependentsSucceeded) if (!isDependentsSucceeded)
dependencyManager.getGameRepository().getVersionRoot(name).delete(); 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))); 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(); 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) { public final Task then(Task b) {
return then(convert(b)); return then(convert(b));
} }
@@ -285,13 +273,29 @@ public abstract class Task {
return new CoupleTask(this, b, true); 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>() { * Returns a new TaskResult that, when this task completes
TaskResult<R> then; * 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 @Override
public Collection<? extends Task> getDependents() { public Collection<? extends Task> getDependents() {
@@ -300,7 +304,7 @@ public abstract class Task {
@Override @Override
public void execute() throws Exception { public void execute() throws Exception {
then = taskSupplier.get().storeTo(this::setResult); then = fn.get().storeTo(this::setResult);
} }
@Override @Override
@@ -318,17 +322,87 @@ public abstract class Task {
return new CoupleTask(this, b, false); 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);
} }
// T, K here is necessary, or javac cannot infer type of failure @Override
public final <T extends Exception, K extends Exception> Task finalized(Scheduler scheduler, ExceptionalRunnable<T> success, ExceptionalConsumer<Exception, K> failure) { public Scheduler getScheduler() {
return finalized(scheduler, (isDependentsSucceeded, exception) -> { 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());
}
/**
* 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 (isDependentsSucceeded) {
if (success != null) if (success != null)
try { try {

View File

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

View File

@@ -29,35 +29,55 @@ import java.util.function.Consumer;
* *
* @author huangyuhui * @author huangyuhui
*/ */
public abstract class TaskResult<V> extends Task { public abstract class TaskResult<T> extends Task {
private V result; private T result;
private Consumer<V> resultConsumer; private Consumer<T> resultConsumer;
@Override @Override
public TaskResult<V> setName(String name) { public TaskResult<T> setName(String name) {
super.setName(name); super.setName(name);
return this; 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; return result;
} }
public void setResult(V result) { protected void setResult(T result) {
this.result = result; this.result = result;
if (resultConsumer != null) if (resultConsumer != null)
resultConsumer.accept(result); 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; return this;
} }
public <R, E extends Exception> TaskResult<R> thenTaskResult(ExceptionalFunction<V, TaskResult<R>, E> taskSupplier) { /**
return new TaskResult<R>() { * Returns a new TaskResult that, when this task completes
TaskResult<R> then; * 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 @Override
public Collection<? extends Task> getDependents() { public Collection<? extends Task> getDependents() {
@@ -66,7 +86,7 @@ public abstract class TaskResult<V> extends Task {
@Override @Override
public void execute() throws Exception { public void execute() throws Exception {
then = taskSupplier.apply(TaskResult.this.getResult()).storeTo(this::setResult); then = fn.apply(TaskResult.this.getResult()).storeTo(this::setResult);
} }
@Override @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) { * Returns a new Task that, when this task completes
return thenVoid(Schedulers.defaultScheduler(), task); * 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 class Subtask<R> extends TaskResult<R> {
private final Scheduler scheduler; 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.scheduler = scheduler;
this.callable = callable; this.callable = callable;