vaisakh
vaisakh

Reputation: 1031

How to make async/await wait for an Observable to return

Fairly new to Angular, and struggling with Promises, Observables and async/await.

However, this doesn't seem to work. I have a feeling this has something to do with how Observables/subscribe works, but I am unable to solve it.

Code snippet:

    initPage() {
      fetchCurrentUserDetails().then((user) => { //tasks dependent on current user
        //task 1
        //task 2
      });
    }

    fetchCurrentUserDetails(): Promise<any> {
      return Promise.resolve((async () => {
        let currentUser = this.global.getUser();// check if user is defined already
        let userId: string = sessionStorage.getItem('userid');

        if (currentUser == undefined) {
          let initProfile = new Promise(resolve => resolve(this.fetchDetailsFromDB(userId)));
          const profile: any = await initProfile; //Waits, but returns before the Observable comes back

          let user = new User();
          // initialize user with the fetched values
          user.id = profile.id; // Undefined, since value not returned yet
          user.name = profile.user_name; // Undefined, since value not returned yet
          // Set this user in a global variable
          this.global.setUser(user);
       }

       return this.global.getUser();
      })());
    }

    fetchDetailsFromDB(userId: string) {
      //callProfileService has nothing but the http.get statement
      this.callProfileService(userId).subscribe((response) => {
        let profile = response.body.response.data.user;
         return profile;
      });
    }


Edit: adding how I tried with toPromise:

    fetchDetailsFromDB(userId: string) {
      this.callUserProfileService(userId).toPromise().then((response) => {
       let profile = response.body.response.data.user;
       return profile;
    });

Is this the right way to do this? If so, how to make the await wait for the Observable to return?

Upvotes: 3

Views: 11564

Answers (2)

Dmitry Grinko
Dmitry Grinko

Reputation: 15204

you can use callback

initPage() {
    fetchCurrentUserDetails((user) => {
        //task 1
        //task 2
    });
}

fetchCurrentUserDetails(callback) {
    const currentUser = this.global.getUser();

    if (!currentUser) {
        const userId: string = sessionStorage.getItem('userid');
        return this.callProfileService(userId).subscribe((response) => {
            const profile = response.body.response.data.user;
            let user = new User();
            user.id = profile.id;
            user.name = profile.user_name;
            this.global.setUser(user);
            return callback(user);
        });
    } else {
        return callback(currentUser);
    }
}

Upvotes: 2

trincot
trincot

Reputation: 350167

Indeed, you need the toPromise() method, but don't forget to return that promise (the return in the callback is not enough -- the function fetchDetailsFromDB needs to return a promise).

On the rest of your code: it is an antipattern to use Promise.resolve and new Promise like that: as a rule of thumb, don't create a new promise with either of these when you already have a promise to work with (e.g. from an API function call).

So here is how you could do it with async methods:

async fetchCurrentUserDetails(): Promise<any> {
//^^^^
    let currentUser = this.global.getUser();
    if (currentUser == undefined) {
      const userId: string = sessionStorage.getItem('userid');
      const profile: any = await this.fetchDetailsFromDB(userId);
      currentUser = new User();
      currentUser.id = profile.id;
      currentUser.name = profile.user_name;
      this.global.setUser(currentUser);
   }
   return currentUser;
}

async fetchDetailsFromDB(userId: string): Promise<any> {
   const response = await this.callUserProfileService(userId).toPromise();
   return response.body.response.data.user;
};

Upvotes: 6

Related Questions