andras__

RxJS abomination

Mar 15th, 2019
145
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. import { Observable, of, ReplaySubject, Subject } from 'rxjs';
  2. import { map } from 'rxjs/operators';
  3.  
  4. // Observable returns true, if the action has been successfully executed
  5. const tryLogin = (id: number): Observable<boolean> => {
  6.     const result = new ReplaySubject<boolean>();
  7.  
  8.     // Operation 1
  9.     loadUserFromDB(id)
  10.         .subscribe(
  11.             user => {
  12.                 // Early stop 1 - true
  13.                 if (user.name === 'Sarah' || user.id === 0) {
  14.                     result.next(true);
  15.                     result.complete();
  16.                     return;
  17.                 }
  18.  
  19.                 // Operation 2, dependent on Operation 1
  20.                 checkMasterData(user)
  21.                     .subscribe(
  22.                         loginInformation => {
  23.                             const {userId, lastLogin} = loginInformation;
  24.  
  25.                             // Early stop 2 - false
  26.                             if (lastLogin === 'LAST_YEAR') {
  27.                                 result.next(false);
  28.                                 result.complete();
  29.                                 return;
  30.                             }
  31.  
  32.                             // Operation 3, dependent on Operation 1 and Operation 2 's success
  33.                             createNewUserId({attemptedUserId: userId})
  34.                                 .subscribe(
  35.                                     // Operation N...
  36.                                     // This function also has a similar structure to `tryLogin`
  37.                                     newId => createAccountFromLogin(newId, result),
  38.                                     error => result.error(error)
  39.                                 );
  40.                         },
  41.                         error => {
  42.                             result.error(error);
  43.  
  44.                             // Sidenote: No idea why complete is called
  45.                             result.complete();
  46.                         }
  47.                     );
  48.             },
  49.             error => result.error(error)
  50.         );
  51.  
  52.     return result;
  53. };
  54.  
  55. const createAccountFromLogin = (userId: number, result: Subject<boolean>): void => {
  56.     // Converting Observable to Subject in these functions
  57.     const accountResult = new ReplaySubject();
  58.  
  59.     const subscription = accountResult.subscribe(
  60.         account => {
  61.             setTimeout(() => {
  62.                 subscription.unsubscribe();
  63.  
  64.                 if (account === undefined) {
  65.                     result.next(false);
  66.                     result.complete();
  67.                 } else if (account.name === 'Admin') {
  68.                     result.next(true);
  69.                     result.complete();
  70.                 }
  71.  
  72.                 // There is some undefined behaviour at some places
  73.                 // - no result called, but let's aim for false otherwise
  74.                 else {
  75.                     result.next(false);
  76.                     result.complete();
  77.                 }
  78.             });
  79.         },
  80.         error => {
  81.             result.error(error);
  82.             result.complete();
  83.         }
  84.     );
  85.  
  86.     // I have no words
  87.     createNewAccount({id: userId, name: 'Test'}, accountResult);
  88. };
  89.  
  90. const createNewAccount = (input: User, result: Subject<Account | undefined>): void => {
  91.     // The same subject-styled something as always, this can also have hierarchy
  92.     // just like the original function
  93.     const subscription = createNewAccountExternal(input)
  94.         .subscribe(
  95.             account => {
  96.                 setTimeout(() => {
  97.                     subscription.unsubscribe();
  98.  
  99.                     const {user} = account;
  100.                     if (user.id < 0) {
  101.                         result.next(undefined);
  102.                     } else {
  103.                         result.next(account);
  104.                     }
  105.                     result.complete();
  106.                 });
  107.             },
  108.             error => {
  109.                 result.error(error);
  110.             }
  111.         );
  112. };
  113.  
  114. // External function and type, dummy operations - only declarations matter
  115. type User = { id: number, name: string };
  116. const loadUserFromDB = (input: number): Observable<User> =>
  117.     of(input).pipe(map(id => ({id, name: 'Josh ' + id})));
  118.  
  119. type LoginInformation = { userId: number, lastLogin: string };
  120. const checkMasterData = (input: User): Observable<LoginInformation> =>
  121.     of(input).pipe(map(user => ({userId: user.id, lastLogin: 'yesterday', userAgent: 'Linux'})));
  122.  
  123. type Account = { user: User };
  124. const createNewAccountExternal = (input: User): Observable<Account> =>
  125.     of(input).pipe(map(user => ({user})));
  126.  
  127. const createNewUserId = (input: { attemptedUserId: number }): Observable<number> =>
  128.     of(input.attemptedUserId + 1);
RAW Paste Data