Chaining of Promises via #flatMap() in Play Contro

2019-06-03 10:10发布

问题:

I'd like to ask if what I'm doing is suitable or if there is a better/more efficient/simpler way.

Scenario:

  1. User logs in with e-mail-address and password, gets routed to the login()-action
  2. If the user is already in the DB and has a password hashed, authenticate with the DB-user and return result-promise
  3. If user is not in the DB or has no password-hash, do a web-service request with the login-data and parse the status from the response -> return a Status-instance
  4. Check the status of the user and return a result-promise

Code:

public Promise<Result> login() {
    Promise<User> userPromise = Promise.promise(() -> User.findByName(login.emailAddress));
    return userPromise.flatMap(user -> {
        if (user != null && user.hasPassword()) {
            if (user.authenticate(login.password)) {
                return Promise.pure(ok("login successful")));
            }
            // password did not match
            return Promise.pure(unauthorized());
        }

        // user has no password-hash stored, so do a webservice-request instead
        final WSRequestHolder holderWithParams =
                getWSRequestHolderForUserStatus(login.emailAddress, login.password);

        final Promise<Status> statusPromise =
                holderWithParams.get().flatMap(this::parseStatusFromResponse);

        return statusPromise.flatMap(status -> {
            if (status != null) {
                if (status.isValid()) {
                    return Promise.pure(ok("login successful")));
                }
                // not a valid status, return unauthorized
                return Promise.pure(unauthorized());
            }
            return Promise.pure(badRequest("response parsing error");
        });
    });
}

My questions:

  1. Am I doing anything wrong or unnecessary, and if yes: what is the correct, easier or more efficient way?
  2. Did I understand the use of Promises and flatMaps, am I using it correctly or "over-using" it?

回答1:

.flatMap is for when some return value of the function you give to it returns a Promise itself. If all you do is return non-futures, then you can just use .map, so the last .flatMap does not seem to need be a .flatMap since you just wrap all the results values in Promise.pure, with .map you can just return those values without further boxing them in a Promise.

The function you give to userPromise.flatMap on the other hand will return a Promise so it must be a flatMap or else it in turn will return Promise<Promise<Result>> which you cannot return from your controller action.