nus-cs2030 / 2021-s2

2 stars 2 forks source link

CS2030 AY19/20 Special Term Q9: CompletableFuture #298

Open jcng2308 opened 3 years ago

jcng2308 commented 3 years ago

CompletableFuture?

Description

I understand that CompletableFuture has .thenApply(r -> r.body()) and .sendAsync(). But in this context how do you even implement so it works? 2021-04-22 (2)

Suggested Answer (if any):

` 2021-04-22 (4)

gracefour11 commented 3 years ago

firstly, i don't think sendAsync should be used here since sendAsync is part of Class HttpClient in Java API. HttpClient is not used here I believe. I think supplyAsync from CompletableFuture in Java APi would be more applicable in this case.

so userNameExist and isValidPassword could be slowing down the userLogin function thus need to do CompletableFuture supply async (maybe need to do CompletableFuture.allOf) for them.

I'm not so sure on the doLogin function, anybody has thoughts on whether or not need to do CompletableFuture for this?

ivankqw commented 3 years ago

firstly, i don't think sendAsync should be used here since sendAsync is part of Class HttpClient in Java API. HttpClient is not used here I believe. I think supplyAsync from CompletableFuture in Java APi would be more applicable in this case.

so userNameExist and isValidPassword could be slowing down the userLogin function thus need to do CompletableFuture supply async (maybe need to do CompletableFuture.allOf) for them.

I'm not so sure on the doLogin function, anybody has thoughts on whether or not need to do CompletableFuture for this?

Yep, I agree with you that supplyAsync will be more applicable. For me, I ran userNameExist and isValidPassword asynchronously and did a thenCombine to get the condition for the next step.

For doLogin, I don't think that needs to be run asynchronously since it is encapsulated in the logic of several if elses so it looks more sequential to me than an asynchronous task

somenobody98 commented 3 years ago

The way I understood this question was that the most time-consuming part was actually obtaining the usernameExists(username) and isValidPassword(password). So ideally, the CompletableFuture should only be wrapped around this.

However, the question did ask for the return type to be CompletableFuture, so I just wrapped the entire "else" body into a CompletableFuture.supplyAsync(() -> ...), which seems weird to me but it is the only logical way to me. As for the "if" body, I just created the new LoginStatus in the supplyAsync.

ireneliee commented 3 years ago

![Uploading Screen Shot 2021-04-22 at 11.40.55 PM.png…]()

jiarong15 commented 3 years ago

Hi, one way that you can go about is to make 4 CompletableFutures that gives the respective outputs and thereafter using thenCombine while abiding by the boolean conditions. Hope this helps ! :)

chengjiangg commented 3 years ago

Hi, I did it using the following approach but I am not sure if it is correct. Hope I can receive some feedback on my approach.

Screenshot 2021-04-25 at 2 00 06 PM
jordanwwb commented 3 years ago

Hi, I did it using the following approach but I am not sure if it is correct. Hope I can receive some feedback on my approach.

Screenshot 2021-04-25 at 2 00 06 PM

hi, i am confused on when we should be using join() and when we should be using get(). Can someone kindly explain in which instance do we use get() and join() respectively.

Buwoo commented 3 years ago

Hi, I did it using the following approach but I am not sure if it is correct. Hope I can receive some feedback on my approach.

Screenshot 2021-04-25 at 2 00 06 PM

hi, i am confused on when we should be using join() and when we should be using get(). Can someone kindly explain in which instance do we use get() and join() respectively.

The difference between get() and join() is that get() throws checked exceptions, whilst join() doesnt throw checked exceptions. I think most of the time we can just use join()! https://stackoverflow.com/questions/45490316/completablefuture-join-vs-get

e0543492 commented 3 years ago

@jordanwwb i think the difference is in the way both handle exceptions. So either method should work.

Herrekt commented 3 years ago

Hi, I did it using the following approach but I am not sure if it is correct. Hope I can receive some feedback on my approach.

Screenshot 2021-04-25 at 2 00 06 PM

I'm a bit confused at the third if statement. Seeing that cu and cp are completedfutures holding boolean values, wouldn't that just mean that doLogin(cu.get(), cp.get()) is just being doLogin(boolean, boolean)?

ashleyleerx commented 3 years ago
Screenshot 2021-04-27 at 9 15 00 PM

Hi this is what I came up with. Instead of using thenCombine I put the CompletableFutures into a list and join but I'm not very sure

Atem729 commented 3 years ago

I believe we are not supposed to use ".join()" in our answer as it defeats the purpose of the CompletableFuture as the code will be blocked at the ".join()" statement and be unable to continue

chooweixiang98 commented 3 years ago

I believe we are not supposed to use ".join()" in our answer as it defeats the purpose of the CompletableFuture as the code will be blocked at the ".join()" statement and be unable to continue

executing either doLogin or returning (false, "Incorrect Username/ Passward") requires that both usernameExists() and isValidPassword() to be completed first. In that case, you need to use .join() to guarantee that return of the T/F value first isn't it?

whoisjustinngo commented 3 years ago

Hi, I did it using the following approach but I am not sure if it is correct. Hope I can receive some feedback on my approach.

Screenshot 2021-04-25 at 2 00 06 PM

I'm a bit confused at the third if statement. Seeing that cu and cp are completedfutures holding boolean values, wouldn't that just mean that doLogin(cu.get(), cp.get()) is just being doLogin(boolean, boolean)?

Yep I think it's gotta be "if (doLogin(username, pw)) { ... }" instead but the rest of the code looks fine to me

seannzx commented 3 years ago

Adapting from this link https://stackoverflow.com/questions/58999254/completablefuture-how-to-return-first-false-or-wait-until-all-are-completed-to-r

Heres my attempt at answering it, not sure if it's right

CompletableFuture<LoginStatus> userLogin(String username, String password) {
        if (this.loggedIn) {
            return CompletableFuture.completedFuture(new LoginStatus(true, "Already logged in!"));
        } else {
            CompletableFuture<Boolean> cu = CompletableFuture.supplyAsync(() -> usernameExists(username));
            CompletableFuture<Boolean> cp = CompletableFuture.supplyAsync(() -> isValidPassword(password));
            Boolean cUandP = CompletableFuture.allOf(cu, cp).thenApply(x -> cu.join() && cp.join()).join();
            if (cUandP) {
                if (doLogin(username, password)) {
                    return CompletableFuture.completedFuture(new LoginStatus(true, "Login by password."));
                } else {
                    return CompletableFuture.completedFuture(new LoginStatus(false, "No server response"));
                }
            } else {
                return CompletableFuture.completedFuture(new LoginStatus(false, "Incorrect Username/Password"));
            }
        }
    }
seannzx commented 3 years ago

I believe we are not supposed to use ".join()" in our answer as it defeats the purpose of the CompletableFuture as the code will be blocked at the ".join()" statement and be unable to continue

The question is asking for us to come up with a method to speed up the computation of usernameExists(username) and isValidPassword(password) function as in reality, it might run forever to search through the database and determine if the username/passwords exists.

So we have to do it asynchronously i.e search both username and password at the same time, instead of doing it sequentially by checking if the username exists, followed by the existence of the password (imagine if it takes 3 hours to find the username). This is done so by calling CompletableFuture.supplyAsync() for both functions.

CompletableFuture<Boolean> cu = CompletableFuture.supplyAsync(() -> usernameExists(username));
CompletableFuture<Boolean> cp = CompletableFuture.supplyAsync(() -> isValidPassword(password));

When we execute the above two lines of code asynchronously, these 2 codes are propagated to separate threads to compute, while the main thread is still running the userLogin function. Because the main thread uses the result from usernameExists(username) and isValidPassword(password), the main thread has to wait for the other 2 threads to complete. So join() is needed for both.

laitzheng commented 3 years ago

Adapting from this link https://stackoverflow.com/questions/58999254/completablefuture-how-to-return-first-false-or-wait-until-all-are-completed-to-r

Heres my attempt at answering it, not sure if it's right

CompletableFuture<LoginStatus> userLogin(String username, String password) {
        if (this.loggedIn) {
            return CompletableFuture.completedFuture(new LoginStatus(true, "Already logged in!"));
        } else {
            CompletableFuture<Boolean> cu = CompletableFuture.supplyAsync(() -> usernameExists(username));
            CompletableFuture<Boolean> cp = CompletableFuture.supplyAsync(() -> isValidPassword(password));
            Boolean cUandP = CompletableFuture.allOf(cu, cp).thenApply(x -> cu.join() && cp.join()).join();
            if (cUandP) {
                if (doLogin(username, password)) {
                    return CompletableFuture.completedFuture(new LoginStatus(true, "Login by password."));
                } else {
                    return CompletableFuture.completedFuture(new LoginStatus(false, "No server response"));
                }
            } else {
                return CompletableFuture.completedFuture(new LoginStatus(false, "Incorrect Username/Password"));
            }
        }
    }

I'm not quite sure of how to use CompletableFuture.allOf(). Can we use thenApply() after CompletableFuture.allOf() since its return type is CompletableFuture Void?

seannzx commented 3 years ago

Adapting from this link https://stackoverflow.com/questions/58999254/completablefuture-how-to-return-first-false-or-wait-until-all-are-completed-to-r Heres my attempt at answering it, not sure if it's right

CompletableFuture<LoginStatus> userLogin(String username, String password) {
        if (this.loggedIn) {
            return CompletableFuture.completedFuture(new LoginStatus(true, "Already logged in!"));
        } else {
            CompletableFuture<Boolean> cu = CompletableFuture.supplyAsync(() -> usernameExists(username));
            CompletableFuture<Boolean> cp = CompletableFuture.supplyAsync(() -> isValidPassword(password));
            Boolean cUandP = CompletableFuture.allOf(cu, cp).thenApply(x -> cu.join() && cp.join()).join();
            if (cUandP) {
                if (doLogin(username, password)) {
                    return CompletableFuture.completedFuture(new LoginStatus(true, "Login by password."));
                } else {
                    return CompletableFuture.completedFuture(new LoginStatus(false, "No server response"));
                }
            } else {
                return CompletableFuture.completedFuture(new LoginStatus(false, "Incorrect Username/Password"));
            }
        }
    }

I'm not quite sure of how to use CompletableFuture.allOf(). Can we use thenApply() after CompletableFuture.allOf() since its return type is CompletableFuture Void?

Screenshot 2021-04-28 at 11 10 50 AM

Based on the picture, I think you can. The result of the thenApply(Function<? super void, ? extends U>) will map the CompletableFuture<Void> to a CompletableFuture<? extends U>.

Since the function being passed in is x -> cu.join() && cp.join(), the result of cu.join() && cp.join() will be the ? extends U.

Though Im not sure if the argument to the function can be any arbitrary variable. Following the stackoverflow link would be __ -> cu.join() && cp.join()