LiveLoveApp logo

Solution - Retry Operator

Solution

import { fromEvent, throwError } from 'rxjs';
import { ajax } from 'rxjs/ajax';
import { catchError, finalize, map, mergeMap, retry } from 'rxjs/operators';

/** The UserResponse represents the shape of the response from the API. */
interface UserResponse {
  data: {
    id: number;
    email: string;
    first_name: string;
    last_name: string;
    avatar: string;
  };
}

/** The random function returns a random integer inclusively between `min` and `max`. */
function random(max: number, min: number): number {
  return Math.floor(Math.random() * (max - min + 1)) + min;
}

const output = document.getElementById('output') as HTMLTextAreaElement;
const btn = document.getElementById('btn') as HTMLButtonElement;

fromEvent(btn, 'click')
  .pipe(
    map(() => random(10, 15)),
    mergeMap((id) =>
      ajax.getJSON<UserResponse>(`https://reqres.in/api/users/${id}`).pipe(
        map((response) => response.data),
        catchError((error) => {
          output.value += `\n\n${error.message}`;
          output.scrollTop = output.scrollHeight;
          return throwError(error);
        })
      )
    ),
    retry(4),
    finalize(() => {
      btn.classList.add('cursor-not-allowed');
      btn.classList.add('opacity-50');
    })
  )
  .subscribe({
    error: (e) => console.error('observer', e),
    next: (value) => {
      output.value += `\n\n${JSON.stringify(value, null, 2)}`;
      output.scrollTop = output.scrollHeight;
    },
    complete: () => console.log('complete')
  });

This was a fun exercise where we are bringing together multiple operators for error handling.

Let's review the code above:

  • First, we use the fromEvent() operator to add an event listener to the btn element's click event.
  • We then map() the MouseEvent to a random integer between 10 and 15.
  • Using the mergeMap() operator we merge the output of the ajax.getJSON() Observable. When we have successfully fetched a user we use the map() operator to return the nested data property from the response object.
  • When an error occurs with the network request the catchError() operator receives the error. We update the output textarea value property with the error message, scroll the textarea to the bottom, and return a new Observable created by the throwError() operator with the error value.
  • When an error notification occurs the retry() operator will prevent the error from being emitted to the Observer and completing the Observable. The use of retry(4) with the count argument set to 4 will allow for the click event Observable to retry four additional times. Once the count is decremented to -1 the error is emitted to the Observer.
  • Finally, we use the finalize() operator to disable the btn element.