Let’s assume you have to invoke two REST APIs and then combine the result. You can run them one by one and then accumulate the result. It looks easy. However, if each call takes up some time, the total time would be pretty significant.
However, we can reduce the time if we can run these two asynchronously, making them parallel. That’s where CompletableFuture
comes into play.
Let’s see a code-
package com.bazlur;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class Day014 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
var stockExchangeService = new StockExchangeService();
var futureCAD = CompletableFuture.supplyAsync(stockExchangeService::getBitcoinValueInCAD);
var futureUSD = CompletableFuture.supplyAsync(stockExchangeService::getBitcoinValueInUSD);
var combined = futureCAD.thenCombine(futureUSD, (cad, usd) -> Stream.of(cad, usd)
.flatMap(Optional::stream)
.collect(Collectors.joining(", ")));
System.out.println("combined = " + combined.get());
}
}
The stockExchange Service:
package com.bazlur;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Element;
import java.io.IOException;
import java.util.Optional;
public class StockExchangeService {
public static final String GOOGLE_FINANCE_QUOTE_BTC_CAD = "https://www.google.com/finance/quote/BTC-CAD";
public static final String GOOGLE_FINANCE_QUOTE_BTC_USD = "https://www.google.com/finance/quote/BTC-USD";
public static final String CAD_TEXT = "Bitcoin to Canadian dollar";
public static final String USD_TEXT = "Bitcoin to United States Dollar";
public Optional<String> getBitcoinValueInCAD() {
return getBitConValue(GOOGLE_FINANCE_QUOTE_BTC_CAD, CAD_TEXT);
}
public Optional<String> getBitcoinValueInUSD() {
return getBitConValue(GOOGLE_FINANCE_QUOTE_BTC_USD, USD_TEXT);
}
private Optional<String> getBitConValue(String url, String textToFind) {
var connect = Jsoup.connect(url);
try {
var document = connect.get();
var select = document.select("h2");
return select.stream()
.filter(element -> element.text().contains(textToFind))
.map(Element::parent)
.map(Element::text)
.findFirst();
} catch (IOException e) {
return Optional.empty();
}
}
}
I have used JSoup to parse the text and exact the desired value from it.
for copy/paste pleasure: https://github.com/rokon12/100DaysOfJava/blob/main/src/main/java/com/bazlur/Day014_2.java