我的异步调用做错了什么?

What am i doing wrong with my async call?

我有这段代码:

public static void main(String[] args) throws UnirestException {
    ArrayList<Stock> listStock 
             = getAllAvailableStocks("https://api.iextrading.com/1.0/ref-data/symbols");
    //doing more actions after the one before, using the data from the listStock etc.
}


private static ArrayList<Stock> getAllAvailableStocks(String url) {
    ArrayList<Stock> stocks = new ArrayList<Stock>();
    Future<HttpResponse<JsonNode>> future = Unirest.get(url)
              .header("accept", "application/json")
              .asJsonAsync(new Callback<JsonNode>() {
                public void failed(UnirestException e) {
                    System.out.println("The request has failed");
                }

                public void completed(HttpResponse<JsonNode> response) {
                     ObjectMapper objectMapper = new ObjectMapper();
                    try {
                        listStock = objectMapper.readValue(response.getRawBody(), new TypeReference<List<Stock>>(){});
                    } catch (Exception e) {
                        System.out.println("all is fucked");
                    }   
                    return listStock;
                }

                public void cancelled() {
                    System.out.println("The request has been cancelled");
                }

            });
}

我是java的新手,我想做以下事情:

1) 我想做异步调用来获取和提取股票列表,只有在请求完成后我想在 main 方法中做接下来的事情。

2) 如何从我构建的方法中提取数据以便我可以在方法之外使用数据?

3) 如果我需要执行以下操作:

getAllAvailableStocks("https://api.iextrading.com/1.0/ref-data/symbols",new Callback<JsonNode>() {
            public void failed(UnirestException e) {
                System.out.println("The request has failed");
            }

        public void completed(HttpResponse<JsonNode> response) {
             ArrayList<Stock> listStock = new ArrayList<Stock>();
             ObjectMapper objectMapper = new ObjectMapper();
             int code = response.getStatus();
             System.out.println(code);
            try {
                listStock = objectMapper.readValue(response.getRawBody(), new TypeReference<List<Stock>>(){});
            } catch (Exception e) {

            }   
            System.out.println(listStock);
        }

        public void cancelled() {
            System.out.println("The request has been cancelled");
        }

    });
}



private static Future<HttpResponse<JsonNode>> getAllAvailableStocks(String url,Callback<JsonNode> cb) {
    return Unirest.get(url)
              .header("accept", "application/json")
              .asJsonAsync(cb);
}

或者类似的东西,它让代码变得可怕,当我想在之后做更多的异步请求时,我这里有一个回调地狱,有什么办法可以避免它吗?我在这里有什么选择?

我认为你混淆了 asnychronoussynchronous.

如果你

want to do async call to get and extract a list of stocks, only after the request completed I want to do the next things in the main method

那么你实际上想要执行一个同步调用。

一个异步调用将执行请求,然后做其他事情(与请求无关),在将来的某个时候你会得到请求的结果并处理它。

要执行 同步 调用,这可能是您想要的,请尝试像这样调整您的代码:

private static ArrayList<Stock> getAllAvailableStocks(String url) {
    ArrayList<Stock> stocks = new ArrayList<Stock>();
    Future<HttpResponse<JsonNode>> future = Unirest.get(url)
              .header("accept", "application/json")
              .asJsonAsync(new Callback<JsonNode>() {
                public void failed(UnirestException e) {
                    System.out.println("The request has failed");
                }

                public void completed(HttpResponse<JsonNode> response) {
                    System.out.println("The request succeeded");
                }

                public void cancelled() {
                    System.out.println("The request has been cancelled");
                }
            });

    HttpResponse<JsonNode> response = future.get(); // NOTE: This call is blocking until the request is finished
    if (response != null && response.getStatus() == 200) {
        JsonNode body = response.getBody();
        // TODO Parse body and add items to `stocks`
    }
    return stocks;
}

这个方法可以这样使用:

ArrayList<Stock> stocks = getAllAvailableStocks(...);
stocks.forEach(x -> System.out.println(x));

编辑

如果你想在不提供回调的情况下异步处理结果,你可以使用 CompletableFuture。将以下代码段作为不处理不成功调用的起点。

private static CompletableFuture<ArrayList<Stock>> getAllAvailableStocks(String url) {
    CompletableFuture<ArrayList<Stock>> result = new CompletableFuture<>();
    Future<HttpResponse<JsonNode>> future = Unirest.get(url)
              .header("accept", "application/json")
              .asJsonAsync(new Callback<JsonNode>() {
                public void failed(UnirestException e) {
                    System.out.println("The request has failed");
                }

                public void completed(HttpResponse<JsonNode> response) {
                    System.out.println("The request succeeded");
                    ArrayList<Stock> stocks = new ArrayList<Stock>();
                    if (response != null && response.getStatus() == 200) {
                        JsonNode body = response.getBody();
                        // TODO Parse body and add items to `stocks`
                    }
                    result.complete(stocks);
                }

                public void cancelled() {
                    System.out.println("The request has been cancelled");
                }
            });

    return result;
}

可以使用的方法如下:

CompletableFuture<ArrayList<Stock>> stocksFuture = getAllAvailableStocks(...);
stocksFuture.thenAccept((stocks) -> {
    // NOTE: This will be called after and only if the request succeeded
    stocks.forEach(x -> System.out.println(x));
});

System.out.println("This is probably executed before the above request finished.");

Thread.sleep(10000); // If you are calling from your `main` method: Prevent JVM exit