Home > Net >  issues with return value from an observable subscription
issues with return value from an observable subscription

Time:07-23

I have a function getHash that retrieves a hash value that I need from a remote api server. I am using the following code for it, which uses the auth0 service to retrieve the jwt token, attach it to the request and then return the http.get Observable

The issue I am facing now is that this function returns a subscription which when I try to read using .subscribe it returns the error subscribe is not a funciton

To solve that I tried to return let res = await this.http.get(url, httpOptions).toPromise() instead of the simple return value that I have right now but that just returns an undefined response.

I am not sure whats happening right now, is there something wrong with the way I am returning inside subscribe inside the getHash() function? Any help would be appreciated?

getHash(): any {
        return this.authService.getAccessTokenSilently()
        .subscribe(jwt => {
            let httpOptions = {
                headers: new HttpHeaders({
                    'Content-Type': 'application/json',
                    'Authorization': `Bearer ${jwt}`
                })
            };
            const decodedToken = window.atob(jwt.split('.')[1]);
            const jwtoken = JSON.parse(decodedToken);
            const url = `${commonEnv.baseUrl}/${jwtoken.sub}/hash`
  
            return this.http.get(url, httpOptions)
           
        })
    }

CodePudding user response:

Your code in getHash is performing some asynchronous work and therefore you can not expect that the function getHash returns the value you want.

What it can return is an Observable which notifies your hash when it becomes available. As in any asyn implementation, you have to pass a function that will process the value (the hash in your case) when it becomes available.

While this is generally true for any async processing, in your case, using Observable, you could restructure your code along these lines

// the function returns an Observable that has to be subscribed by the caller
// of the function
getHash(): Observable<any> {
  // you start from the getAccessTokenSilently method which returns an
  // Observable which notifies the jwt token
  return this.authService.getAccessTokenSilently().pipe(
    // we start a pipe to transform the Observable returned by getAccessTokenSilently
    // the transformation is a concatenation: we concatenate the 
    // Observable returned by getAccessTokenSilently with the Observable
    // returned by http.get - this means that we wait for the Observable 
    // returned by getAccessTokenSilently to notify the jwt value and then
    // we kick off the Observable returned by http.get
    // this transformation is achieved using the concatMap operator like this
    concatMap(jwt => {
       let httpOptions = {
       headers: new HttpHeaders({
           'Content-Type': 'application/json',
           'Authorization': `Bearer ${jwt}`
          })
       };
       const decodedToken = window.atob(jwt.split('.')[1]);
       const jwtoken = JSON.parse(decodedToken);
       const url = `${commonEnv.baseUrl}/${jwtoken.sub}/hash`
    })
}

Now getHash returns an Observable which will notify the hash when available.

Whoever needs to use this hash has to subscribe to this Oservable like this

getHash().subscribe(hash => {
   // do whatever is necessary with the hash
})

CodePudding user response:

In JavaScript Asynchronous value can never* be returned synchronously

Because JS is pretty much single threaded and relies on an event loop (itself a runtime system running on the same thread) to manage callbacks, waiting for an async value pretty much always deadlocks.

All that to say, you can never return the value inside a promise or an observable directly.

So what do you do instead? You have two options:

  1. Continuations: Don't return the value, just write the code that should be run once the value returns and pass that forward.
  2. Futures: You return a promise or an observable that will eventually hold the value you want to return, rely on whoever called your function to figure out how they want to get that value (using either 1 o 2).

Pass a continuation:

// Instead of returning, do whatever useHash says
function getHash(useHash: (hash:any) => void): void {
  this.authService.getAccessTokenSilently().pipe(
    switchMap(jwt => {
      let httpOptions = {
          headers: new HttpHeaders({
              'Content-Type': 'application/json',
              'Authorization': `Bearer ${jwt}`
          })
      };
      const decodedToken = window.atob(jwt.split('.')[1]);
      const jwtoken = JSON.parse(decodedToken);
      const url = `${commonEnv.baseUrl}/${jwtoken.sub}/hash`

      return this.http.get(url, httpOptions)
     
    })
  ).subscribe(
    (response: HashResponse) => useHash(response.hash)
  );
}

Returning an observable

// Return an observable, let the caller decide how to use it
getHash(): Observable<HashResponse> {
  return this.authService.getAccessTokenSilently().pipe(
    switchMap(jwt => {
      let httpOptions = {
          headers: new HttpHeaders({
              'Content-Type': 'application/json',
              'Authorization': `Bearer ${jwt}`
          })
      };
      const decodedToken = window.atob(jwt.split('.')[1]);
      const jwtoken = JSON.parse(decodedToken);
      const url = `${commonEnv.baseUrl}/${jwtoken.sub}/hash`

      return this.http.get(url, httpOptions)
     
    })
  );
}

CodePudding user response:

what this.authService.getAccessTokenSilently() returning? if you want to make another req with this function answer, use switchMap inside a pipe.

Let httpOptions is a var inside the subscription scope without any use.

  • Related