Does it make sense to return shareReplay from defer?

404 Views Asked by At

I was trying to utilize observables in authentication routine in my sample app which I'm building to learn ReactiveX, while looking for examples I've found a neat and also well in-code commented gist (@alxhub, thanks!) that shows how an authentication service along with interceptor could be organized, but I can't wrap my head around what's happening in there, probably due to luck of experience... The relevant portion of code is:

this.refreshToken = Observable.defer(() => {
  // Defer allows us to easily execute some action when the Observable
  // is subscribed. Here, we set the current token to `null` until the
  // refresh operation is complete. This ensures no requests will be
  // sent with a known bad token.
  this.subject.next(null);

return this
  // Next, we refresh the token from the server.
  .doRefreshToken()
  // Set it as the active token.
  .do(token => this.subject.next(token))
  // Drop the value, ensuring this Observable only completes when
  // done and doesn't emit.
  .ignoreElements()
  // Finally, share the Observable so we don't attempt multiple
  // refreshes at once.
  .shareReplay();
});

which then used in interceptor like this:

.catch((err, restart) => {
  // If the request is unauthorized, try refreshing the token before restarting.
  if (err instanceof HttpErrorResponse && err.status === 401) {
    return Observable.concat(this.auth.refreshToken, restart);
  }
  throw err;
})

As I understand defer creates a separate observable for each observer, so why do they bother to shareReplay? In what occasion there will be more than one observer on the newly created observable?

Now, I wanted to to show a login dialog in case of 401, but only one for a batch of requests, and it seems that shareReplay is the way to go, so I thought to put the dialog open code inside doRefreshToken but if it gonna be inside defer I'll get as much dialogs as number of unauthenticated requests I presume, so how I should organize it?

1

There are 1 best solutions below

2
On

I think the shareReplay in refreshToekn doesn't make sense since as you said it is emitted from defer which creates new stream each time. I assume with or without it, the code will run the same.

You can create an observable like below and assign to a variable and attach to the concat. This shareReplay will only emit once.

const openDialog=
   of(true).pipe(map(e=>{
 console.log("open dialog") 
 return e 
}),shareReplay(1))

and in catch

.catch((err, restart) => {
  // If the request is unauthorized, try refreshing the token before restarting.
  if (err instanceof HttpErrorResponse && err.status === 401) {
    return Observable.concat(this.auth.refreshToken,openDialog, restart);
  }
  throw err;
})