RoyNasr
RoyNasr

Reputation: 359

Return value with asynchronous functions in Typescript

I have the following TS function:

CheckRegUser(u: User): boolean {
    let b: boolean = true;
    let toSend = {
      email: u.email
    };
    this.http.post("http://localhost:8080/", toSend).subscribe((data: Valid) => {
      if(!data.validity){
        b = false;
      }
    });
    console.log(b);
    return b;
}

Here I am connecting to an ExpressJS backend and I am getting back a boolean result. This part is working fine, but the problem is that the "return b" statement is executed before that the value of b is changed in "this.http", so b is always true no matter what the response from Express is.

I know that TS and JS are asynchronous and this problem is caused because of that, but I couldn't find the correct method to solve my issue. Any help would be appreciated.

Upvotes: 17

Views: 41729

Answers (3)

Rafiq
Rafiq

Reputation: 11535

you can return the entire document after pulling it from DB

Refer to a Promise-compatible constructor value.

export const getUserWishList = async (user: string): Promise<WishDoc> => {
  let wish = await Wish.findOne({ user });
  if (!wish) {
    wish = Wish.build({
      user,
      list: [],
    });
    await wish.save();
  }
  return wish;
};

Async functions always return a promise.

const wishList = await getUserWishList(req.currentUser?.id!);

Upvotes: 1

brk
brk

Reputation: 50346

You can try with async await

  async CheckRegUser(u: User): Promise<boolean> {

    let toSend = {
      email: u.email
    };
    let k = await this.http.post("http://localhost:8080/", toSend).subscribe((data: Valid){
      let b: boolean = true;
      if(!data.validity){
        b = false;
      }
      return b
    });
    console.log(k);
    return k;
}

Upvotes: 14

mittens pair
mittens pair

Reputation: 1130

I had this common problem when I was just starting to learn Observables/Reactive programming with a Google Maps app as well :D!

Spot on, you realised that asynchronous function hasn't mutated the b boolean flag yet when the function synchronously returns b (that defaulted to true);

To fix this it might be easier to restructure your function to return the Observable to the caller.

and maybe look up Observable chaining or similar concept Promise chaining.

Old example:

CheckRegUser(u: User): boolean {
    const b: boolean = true;
    const toSend = {
      email: u.email
    };

    const httpPost: Observable<aJsonObject> = this.http.post("http://localhost:8080/", toSend)
    const httpPostSubscription: Subscription = httpPost
      .subscribe((data: Valid) => { // This is asynchronous
        if (!data.validity) {
          b = false;
        }
      });
    console.log(b);

    return b; // This is effectively sync returning your default value `b = true`;
  }

It would be better if you could rename your variables to be clearer. b or isDataValid. Might also be good practice to start functional-style with const variables to help avoid mutability issues.

If you're more familiar with Promises, you could also try to promisify the httpClient.post Observable.

General gist of it is to pass the Observable back to the caller and .subscribe it there. When something is returning asynchronously, you should return the asynchronicity all the way back to the top.

Refactor code to reflect these practices

  CheckRegUser(user: User, httpClient: HttpClient): Observable<Valid> {
    // 1. Data to send
    type  EmailPost                  = { email: string }
    const emailJsonToSend: EmailPost = { // I prefer explicit typing wherever possible :tada: :D
      email: user.email
    };

    // 2. POST the data to the web server
    const emailHttpPostObs: Observable<Valid> = httpClient.post("http://localhost:8080/", emailJsonToSend);

    return emailHttpPostObs;
  }

  CallerSubmitUserFunction(user: User, httpClient: HttpClient) {
    // Made some assumptions, please comment and we can work out a better solution
    // Explicitly typed things as an example.

    // 1. You have a user e.g. new User(name:'myUser', email: 'myEmailAddressOrEmailContent@email.com');
    const userToSend: User = user;
    // 2. You POST the user.email and get response.
    const validatedUserDataObs: Observable<Valid> = CheckRegUser(userToSend, httpClient);

    // 3. You check whether the server accepted the data and whether it was valid.
    const validatedUserDataObs: Subscription = validatedUserDataObs
      .subscribe((data: Valid) => {
        // Perform data validation or Error checking here.

        // If data is valid, 
        if (dataValidationFunction()) {
          // Do something here
          // Instead of trying to return the boolean in the asynchronouse function.
          // Directly do something after the server responds (whenever it happens).
        }
      }) // catch Error


    // It would also be better if you could rename your variables to be more self-indicative
    // var dataIsValid

  }

Upvotes: 4

Related Questions