简体   繁体   中英

RxJS: WebSocket reconnection handling

I've created a service to handle a WebSocket connection to send and receive messages, where I'm handling a few cases when a disconnection might happen and how to recover from them, but I'm missing the case where the browser itself (so to speak) goes offline it automatically retries when the navigator.onLine emits a true value again.

So far, by using the retryWhen operator, I'm able to create a new session and restart it (if something goes wrong on the server side), until it reaches a maximum number of retries. But, by using the navigator.onLine (as unreliable as it is, I know, but it works for the browsers we are using) I can restart the reconnection process, when the client goes offline.

I have this online checker observable:

private onlineCheck: Observable<boolean>;

this.onlineCheck = merge(
      of(navigator.onLine),
      fromEvent(window, 'online').pipe(mapTo(true)),
      fromEvent(window, 'offline').pipe(mapTo(false)));

And this is how I'm creating a WebSocket (and handling a reconnection if needed):

public connect(): void {
  // no more than one connection simultaneously
  if (!(this.socket == null)) {
    this.socket.complete();
  }

  this.socket = new WebSocketSubject(this.config);
  this.socket
    .pipe(
      retryWhen((errors) => {
        return errors.pipe(
          tap((error) => { console.log('Error: ', error); }),
          concatMap((e, i) =>
            iif(
              () => i < this.reconnectAttempts,
              of(e).pipe(delay(this.reconnectInterval)),
              throwError('Max amount of retries reached')
            )));
      }))
    .subscribe(
      (message: MessageEvent) => { this.onMessage(message); },
      (error: Event) => { this.onError(error); },
      () => { console.log('completed'); });
  }

How can I combine an onlineCheck subscription with the retryWhen operator, in order to retry it, if the client went offline (and the maximum connection retries is reached) and is now back? If this is not the best approach, could you please suggest another?

Here's a complete StackBlitz example (check the console logs for better feedback): https://stackblitz.com/edit/angular-f4oa3y

Inside your iif true condition, use this observable instead of of(e).pipe(delay(this.reconnectInterval)) :

timer(this.reconnectInterval) // wait for reconnect interval
  .pipe(
    concatMap(() => onlineCheck),
    first(Boolean) // now wait for online to be true
  )

That will emit an observable that will produce a single value after reconnectInterval and onlineCheck is true

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM