skip to Main Content

I have an app that needs to retrieve some properties from a REST call, and then those properties get used at other places in the code. The problem is that sometimes the code tries to use the properties before they are fully retrieved.

@Injectable()
export class PropertiesService
{

  public properties: any = {};

  constructor(
    public http: HttpClient
  )
  {
    let url = Environment.hostUrl + "properties/";
    this.http.get<ApiResponse>(url).subscribe(
    (response) =>
    {
      console.info("Setting Remote Properties", response.message);
      this.properties = response.message;
    });
  }

  public getProperty(propertyName: string)
  {
    return this.properties[propertyName];
  }
}

And then in other places in the code it will call to get a property:

console.info(propertiesService.getProperty("propertyName"));

But if that gets called before the properties response gets back, it will return undefined and I only want the users of the properties to use them once they have been set. Generally the properties response doesn’t take very long to come back (just a few milliseconds), so I was wondering if there was a way to make the "getProperty" method wait until the property is no longer undefined before it returns with the value. But I’m not sure if that is good design and if there is a better way to do it I’d like to know about that.

2

Answers


  1. You can’t return a promise in a class constructor in javascript thus you can’t wait for async code to be executed.

    You can do something like this instead:

     @Injectable()
    export class PropertiesService
    {
    
      public properties: any = {};
      public propPromise: any;
    
      constructor(
        public http: HttpClient
      )
      {
        this.setProperty()
      }
    
      async getProperty(propertyName: string)
      {
        await this.propPromise;
        return this.properties[propertyName];
      }
    
      public setProperty() {
        this.propPromise = new Promise(resolve => {
          let url = Environment.hostUrl + "properties/";
          this.http.get<ApiResponse>(url).subscribe(
          (response) =>
          {
            console.info("Setting Remote Properties", response.message);
            this.properties = response.message;
            resolve()
          });
        })
        
      }
    }
    

    This will set the promise to a class property and we can wait for the promise to resolve before returning the properties.

    Login or Signup to reply.
  2. You could use a variable isLoading like a semaphore to know when the api has successful retrieved the values.

    Then you could use a BehaviourSubject to store in memory the results and get them back whenever you need without calling the api again.

    For ex:

    import {BehaviorSubject, filter, finalize, map, of} from "rxjs";
    
    @Injectable()
    export class PropertiesService {
    
    public isLoading$ = new BehaviorSubject<boolean>(true);
    public properties$ = new BehaviorSubject<any>({});
    
    constructor(
        public http: HttpClient,
        public dialog: DialogService
    )
    {
        let url = Environment.hostUrl + "properties/";
        this.isLoading$.next(true);
        this.http.get<ApiResponse>(url)
        .pipe(finalize(() => this.isLoading.next(false)))
            .subscribe(
                (response) => {
                    console.info("Setting Remote Properties", response.message);
                    this.properties$.next(response.message);
                });
      }
    
      public getProperty(propertyName: string) {
        return this.isLoading$
            .pipe(
                // you return a value only if isLoading is false
                filter((isLoading) => !isLoading),
                // return the desired result
                map(() => this.properties$.getValue()[propertyName])
            );
      }
    }
    

    Then in you code you need to subscribe to get the value

    this.propertiesService.getValue('**you value here**').subscribe(value => console.info(value));
    

    Using observable is the best way to deal with async functions like rest api call.

    Generally the properties response doesn’t take very long to come back
    (just a few milliseconds)

    This is a dangerous observation. You should never rely on async functions speed. Manage them with observables so you can get the values only when you have them.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search