等待 CompletableFuture 完成,然后 return 一个不同的值?

Wait for a CompletableFuture to be complete, then return a different value?

我有一个 CompletableFuture<Void> 调用异步方法,我无法更改其 return 类型或任何相关内容。

我想等这个方法完成(我手动完成),然后return一个String值,我该怎么做?

public String getServer(Player p) {
    FutureServer f = new FutureServer(CompletableFuture.runAsync(() -> {
        sendUTF(p, "GetServer");
        try {
            Thread.sleep(10000); //so the future doesnt complete itself
        } catch (Exception e) {
            e.printStackTrace();
        }
    }), p.getUniqueId().toString());
    serverSet.add(f);
    String server = "";

    //server isn't final so I can't use it in the lambda
    f.getFutureVoid().whenComplete(v -> server = f.getServer()); 

    return server;
}

public class FutureServer {

    private CompletableFuture<Void> futureVoid;
    private String s;
    private String uuid;

    public FutureServer(CompletableFuture<Void> futureVoid, String uuid) {
        this.futureVoid = futureVoid;
        this.uuid = uuid;
    }

    public String getUuid() {
        return uuid;
    }

    public CompletableFuture<Void> getFutureVoid() {
        return futureVoid;
    }

    public boolean hasServer() {
        return s != null;
    }

    public void setServer(String s) {
        this.s = s;
    }

    public String getServer() {
        return s;
    }
}

我想将字符串设置为等于 FutureServer#getServer()(自己的方法),但我需要等到 CompletableFuture<Void> 完成。我该怎么办?

这是被称为异步且不可更改的方法...我使用的异步调用此其他方法的方法是 sendUTF()

@Override
public void onPluginMessageReceived(String s, Player p, byte[] bytes) {
    if (!s.equals("BungeeCord")) return;

    ByteArrayDataInput in = ByteStreams.newDataInput(bytes);
    String subChannel = in.readUTF();

    switch(subChannel) {
        case "GetServer":
            String server = in.readUTF();
            serverSet.stream().filter(f -> f.getUuid().equals(p.getUniqueId().toString())).findFirst().ifPresent(f -> {
                f.setServer(server); //getting the string I need and placing it into this object
                f.getFutureVoid().complete(null); //completing the void future manually
            });
            break;
    }

}

你可以这样做:

final AtomicReference<String> server = new AtomicReference<>("");

f.getFutureVoid().whenComplete(v -> server.set(f.getServer())).get(/* maybe add a timeout */); 

return server.get();

最简单的解决方案就是 join() 那个未来,要么:

public String getServer(Player p) {
    FutureServer f = new FutureServer(CompletableFuture.runAsync(() -> {
        sendUTF(p, "GetServer");
        try {
            Thread.sleep(10000); //so the future doesnt complete itself
        } catch (Exception e) {
            e.printStackTrace();
        }
    }), p.getUniqueId().toString());
    serverSet.add(f);

    return f.getFutureVoid().thenApply(v -> f.getServer()).join(); 
}

可以通过移除 .join() 轻松转换为 return 和 CompletableFuture<String>,或者:

public String getServer(Player p) {
    FutureServer f = new FutureServer(CompletableFuture.runAsync(() -> {
        sendUTF(p, "GetServer");
        try {
            Thread.sleep(10000); //so the future doesnt complete itself
        } catch (Exception e) {
            e.printStackTrace();
        }
    }), p.getUniqueId().toString());
    serverSet.add(f);

    f.getFutureVoid().join();
    return f.getServer();
}