Angular NgRx - эффект продолжения опроса сервиса, вызываемого только в первый раз - PullRequest
2 голосов
/ 17 октября 2019

У меня есть приложение, в котором я только что добавил NgRX, где я хочу использовать эффекты для включения и выключения опроса.

Пример схемы

Я следовал этот пост который выглядел как хороший подход. У меня есть упрощенный пример этого здесь , с большей частью кода в app.effects.ts.

Как и в примере, у меня есть эффекты startPolling$, stopPolling$ иcontinuePolling$, за исключением того, что я использую более новые createEffect фабричные методы.

Кроме того, я переместил delay(2000) выше takeWhile(), как я обнаружил, если вызов службы выдает ошибку,catchError(err => of(appActions.getDataFail(err))) приведет к тому, что эффект перейдет в непрерывный очень быстрый цикл без задержки.

Кнопка запуска и остановки отправляет и останавливает опрос ...

public start() {
    console.log('dispatching start');
    this.store.dispatch(appActions.startPolling());
  }

  public stop() {
    console.log('dispatching stop');
    this.store.dispatch(appActions.stopPolling());
  }

Моя проблема

У меня есть несколько журналов консоли, чтобы мы могли видеть, что происходит.

Когда мы нажимаем кнопку запуска (просто в первый раз ), я вижу начало опроса и продолжаю как ожидалось. Например, я могу видеть следующее снова и снова ...

dispatching start
app effect started polling
app.service.getData
app effect continue polling
app.service.getData
app effect continue polling
app.service.getData
app effect continue polling

Отлично.

И когда я нажимаю на стоп, я вижу

dispatching stop
app effect stop polling

Также правильно.

Теперь проблема заключается в том, что я пытаюсь перезапустить . Если я теперь снова нажму кнопку «Пуск», все, что я вижу, - это начальный эффект опроса начала ...

dispatching start
app effect started polling
app.service.getData

, и код в continuePolling$ больше не вызывается , поэтомуУ меня нет опроса.

У кого-нибудь есть идеи, почему этот эффект не срабатывает в течение секунд? Я просто не могу понять, почему это так.

Заранее спасибо за любую информацию.

[ОБНОВЛЕНИЕ1]

Я думаю, возможно, моя проблема в том, чтоесли для isPollingActive установлено значение false, а takeWhile(() => this.isPollingActive), «останавливается», наблюдаемое больше не активно, т. е. завершено continuePolling$, поэтому перезапуск никогда не будет?

Предполагая это, я попробовал следующее, гдеУ меня есть 2 разные переменные, одна для «приостановки» опроса (например, если я обнаружу приложение в автономном режиме), а другая для отмены (то есть, когда пользователь будет перемещаться из компонента).

Итаквсе мои эффекты теперь становятся ...

    @Injectable()
    export class AppEffects {
      private isPollingCancelled: boolean;
      private isPollingPaused: boolean;

      constructor(
        private actions$: Actions,
        private store: Store<AppState>,
        private appDataService: AppDataService
      ) { }

      public startPolling$ = createEffect(() => this.actions$.pipe(
        ofType(appActions.startPolling),
        tap(_ => console.log('app effect started polling')),
        tap(() => {
          this.isPollingCancelled = false;
          this.isPollingPaused = false;
        }),        
          mergeMap(() =>
            this.appDataService.getData()
              .pipe(                        
                switchMap(data => {              
                  return [appActions.getDataSuccess(data)
                  ];
                  }),
                catchError(err => of(appActions.getDataFail(err)))
              ))
        ));

         public pausePolling$ = createEffect(() => this.actions$.pipe(
            ofType(appActions.pausePolling),
            tap(_ => this.isPollingPaused = true),
            tap(_ => console.log('app effect pause polling')),       
         ));

      public cancelPolling$ = createEffect(() => this.actions$.pipe(
        ofType(appActions.cancelPolling),
        tap(_ => this.isPollingCancelled = true),
        tap(_ => console.log('app effect cancel polling')),
      ));

        public continuePolling$ = createEffect(() => this.actions$.pipe(
          ofType(appActions.getDataSuccess, appActions.getDataFail),    
          tap(data => console.log('app effect continue polling')),  
          takeWhile(() => !this.isPollingCancelled),    
          delay(3000),  

          mergeMap(() =>
            this.appDataService.getData()
              .pipe(   
                delay(3000),  
                tap(data => console.log('app effect continue polling - inner loop')),  
                takeWhile(() => !this.isPollingPaused), // check again incase this has been unset since delay 
                switchMap(data => {              
                  return [appActions.getDataSuccess(data)
                  ];
                  }),
                catchError(err => of(appActions.getDataFail(err)))
              ))
        ));    
    } 

Я бы не рекомендовал запускать вышеописанное, так как при отправке pause polling action эффект, кажется, попадает в бесконечный цикл, и мне приходится убиватьбраузер через диспетчер задач.

Понятия не имею, почему это происходит, но мне кажется, что я далек от решения, чем раньше.

Любая помощь, еще раз, очень признателен

[ОБНОВЛЕНИЕ2]

Я заметил, что не вернулсяубирая любые действия из эффектов паузы и отмены.

Итак, я обновил их, следуя инструкциям ...

 public pausePolling$ = createEffect(() => this.actions$.pipe(
    ofType(appActions.pausePolling),
    tap(_ => this.isPollingPaused = true),
    tap(_ => console.log('app effect pause polling')),
    map(_ => appActions.pausePollingSuccess())
  ));

  public cancelPolling$ = createEffect(() => this.actions$.pipe(
    ofType(appActions.cancelPolling),
    tap(_ => {
      this.isPollingCancelled = true;
      this.isPollingPaused = true;
    }),
    tap(_ => console.log('app effect cancel polling')),
    map(_ => appActions.cancelPollingSuccess())
  ));

Теперь пауза работает нормально, но когда я отправляю appActions.cancelPolling, Я снова вижу, как бесконечный цикл app effect cancel polling регистрируется на консоли.

[ОБНОВЛЕНИЕ3]

Я нашел, почему я получаю бесконечный цикл икак это остановить. Согласно документу здесь , я могу добавить dispatch:false ...

    public cancelPolling$ = createEffect(() => this.actions$.pipe(
        ofType(appActions.cancelPolling),
        tap(_ => {
          this.isPollingCancelled = true;
          this.isPollingPaused = true;
        }),
        tap(_ => console.log('app effect cancel polling')),
      ), { dispatch: false }); // <------ add this

, и это, кажется, исправляет мой бесконечный цикл.

Моя единственная задача сейчасдолжен уметь выяснить, как уметь запускать, останавливать и перезапускать опрос, обрабатывая как успешные вызовы appDataService.getData(), так и исключения;

Я могу заставить его работать для одного или другого(в зависимости от того, куда я положил задержку и время), но не для обоих

[ОБНОВЛЕНИЕ4]

У меня есть последний код здесь .

Запустив его как есть, я получил getData успешно, и, что удивительно, либо действие паузы, либо останова остановит его и позволит перезапустить .. Я удивлен, что действие stop позволяетперезапустить, поскольку я предполагал, что takeWhile(() => !this.isPollingCancelled), отменит эффект.

Кроме того, если true передано getData, это приведет к тому, что его можно увидеть с ошибкой. Опрос продолжается (как хотелось бы, т.е. все еще повторяется даже при ошибке), но как только мы теперь, когда отправляем действие паузы, он НЕ прекращает опрос, и если мы отправляем остановку, он останавливается, но затем не перезапускается. Я не могу выиграть.

[ОБНОВЛЕНИЕ 5]

Я подумал, что, возможно, поскольку эффект продолжения опроса отменяется, я мог бы просто воссоздавать его каждый раз, как показано ниже ..

    import { Injectable, OnInit, OnDestroy } from '@angular/core';
    import { createEffect, Actions, ofType } from '@ngrx/effects';
    import { select, Store } from '@ngrx/store';
    import { mergeMap, map, catchError, takeWhile, delay, tap, switchMap } from 'rxjs/operators';
    import { AppState } from './app.state';
    import { Observable, of } from 'rxjs';
    import { AppDataService } from '../app-data.service';
    import * as appActions from './app.actions';

    @Injectable()
    export class AppEffects {
      private isPollingCancelled: boolean;
      private isPollingPaused: boolean;

      constructor(
        private actions$: Actions,
        private store: Store<AppState>,
        private appDataService: AppDataService
      ) { }

      public startPolling$ = createEffect(() => this.actions$.pipe(
        ofType(appActions.startPolling),
        tap(_ => console.log('app effect started polling')),
        tap(() => {
          this.isPollingCancelled = false;
          this.isPollingPaused = false;
          this.createPollingEffect(); // <--- recreate the effect every time
        }),        
          mergeMap(() =>
            this.appDataService.getData()
              .pipe(                        
                switchMap(data => {              
                  return [appActions.getDataSuccess(data)
                  ];
                  }),
                catchError(err => of(appActions.getDataFail(err)))
              ))
        ));

      public pausePolling$ = createEffect(() => this.actions$.pipe(
        ofType(appActions.pausePolling),
        tap(_ => this.isPollingPaused = true),
        tap(_ => console.log('app effect pause polling')),
      ), { dispatch: false });

      public cancelPolling$ = createEffect(() => this.actions$.pipe(
        ofType(appActions.cancelPolling),
        tap(_ => {
          this.isPollingCancelled = true;
          this.isPollingPaused = true;
        }),
        tap(_ => console.log('app effect cancel polling')),
      ), { dispatch: false });

      public continuePolling$: any;

      private createPollingEffect(): void {
        console.log('creating continuePolling$');
        this.continuePolling$ = createEffect(() => this.actions$.pipe(
          ofType(appActions.getDataSuccess, appActions.getDataFail),
          tap(data => console.log('app effect continue polling')),
          delay(3000),
          takeWhile(() => !this.isPollingCancelled),
          mergeMap(() =>
            this.appDataService.getData(false)
              .pipe(
                tap(data => console.log('app effect continue polling - inner loop')),

                switchMap(data => {
                  return [appActions.getDataSuccess(data)
                  ];
                }),
                catchError(err => of(appActions.getDataFail(err)))
              ))
        ), { resubscribeOnError: true });
      } 
    }

Итак, в startPolling я вызываю this.createPollingEffect(), чтобы создать эффект продолжения опроса.

Однако, когда я пытался это сделать, опрос никогда не начинается.

1 Ответ

0 голосов
/ 17 октября 2019

Используйте это вместо:

public startPolling$ = createEffect(() => this.actions$.pipe(
  ofType(appActions.startPolling),    
  tap(_ => console.log('app effect started polling')),  
  tap(() => this.isPollingActive = true),        
  switchMap(() =>
    this.appDataSurvice.getData()
      .pipe(                        
        exhaustMap(data => {              
          return [appActions.getDataSuccess(data)];
        }),
        catchError(err => of(appActions.getDataFail(err)))
      ))
));
...