У меня есть приложение, в котором я только что добавил 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()
, чтобы создать эффект продолжения опроса.
Однако, когда я пытался это сделать, опрос никогда не начинается.