Search code examples
angulartypescriptasync-awaitpromiseobservable

how to use observables, async/await and subscribe in angular correctly


I have a properties json file in location assets/constants/props.json. In the json file I have one key names someValue and its value is abc.

The json file looks like this:

enter image description here

I have a service file constants.service.ts and the 2 methods in it:

  public getJson(url : string) : Observable<any> {
    return this.http.get(url);
  }

  public someValue() : string {
    return this.getJson("assets/constants/props.json").subscribe(async (data: any) => {
      return await data.someValue;
    });
  }

And also in one of my components I use this value:

  private someValue! : string;

  ngOnInit() {
    this.someValue = this.server.someValue();
  }

In this way I get: Type 'Subscription' is not assignable to type 'string'. error.

I have tried many ways and none of them worked:

  1. make someValue() method returns Promise<string>

  2. make ngOnInit() async method and use await in it

  3. using unsubsribe, toPromise(), toString()...

And much more...

Every solution gave me a different error message, or gave me a wrong value [object object] instead of "abc".

I have tried all the possible options, and I can't get the result I expect.

My question is how to use observables, async, await, subscribe correctly


Solution

  • There is no way you can directly convert either an Observable<string> or a Promise<string> into a string, because it makes no sense semantically.

    Both Observables and Promises represent values that are not available yet but may be provided at some point in the future. Because all JavaScript I/O APIs I am aware of are designed non-blocking, you can't let your process sit around , wait and do nothing (i.e. block) until an I/O operation completes.

    Instead, you register a callback function that will get invoked at a later point in time when a value is available. This is what subscribe in RxJS or then in Promises is for.

    The async and await keywords are just syntax sugar for creating Promises and registering callbaks on them, respectively. Under the hood, async it just transforms the code that follows it into as callback function and registers it at a Promise. So in your example, it does nothing useful at all.

    There are several ways you can get a value from an Observable.

    Here's the simplest:

    ngOnOnit() {
      this.server.someValue().subscribe(value => this.someValue = value);
    }
    

    But this is a bit unwieldy, and there are some caveats with that, such as potential memory leaks.

    There is a better way: Just let Angular deal with it, using AsyncPipe. Just expose the Observable directly to the template and let Angular subscribe to it:

    someValue$: Observable<string>; // The "$" sign is just naming convention to tell Observables and plain values apart
    
    ngOnInit() {
      this.someValue$ = this.server.someValue();
    }
    
    <p>Your value: {{someValue$ | async}}</p>
    

    As a sidenote, all of this will hopefully get much easier with Angular 16 when it gets support for Signals.