rxjs + angular: ошибки, «убивающие» наблюдаемое после ошибки в вызове веб-службы - PullRequest
0 голосов
/ 31 октября 2018

Итак, я начал играть с rxjs, и у меня возник вопрос о том, как лучше сохранить свою наблюдаемую жизнь после получения ошибки от вызова веб-службы.

Перед показом кода вот мой текущий сценарий: угловой компонент, который должен загрузить начальный список, который разбит на страницы и который также может быть отфильтрован путем изменения элемента в комбо. Чтобы решить эту проблему с помощью rxjs, я подумал о слиянии двух наблюдаемых: одна обрабатывает событие изменения выбора, а другая используется для загрузки большего количества элементов. Вот код, который я использую:

const filtro$ = this.estadoPedido.valueChanges.pipe(
    distinctUntilChanged(),
    tap(_ => {
        this._paginaAtual = 0;
        this.existemMais = true;
    }),
    startWith(this.estadoPedido.value),
    map(estado => new DadosPesquisa(this._paginaAtual,
        this._elemsPagina,
        estado,
        false))
);

Всякий раз, когда выбор изменяется, я в итоге сбрасываю глобальный счетчик страниц (оператор tap), и, поскольку я хочу получить начальную загрузку, я также использую оператор startWith. Наконец, я преобразую текущее состояние в объект, который имеет все необходимые значения для загрузки значений.

У меня также есть Тема, которая используется всякий раз, когда нажимается кнопка загрузки дополнительных элементов:

dataRefresh$ = new Subject<DadosPesquisa>();

И эти две наблюдаемые объединены, чтобы у меня был единственный путь для вызова моего веб-сервиса:

this.pedidosCarregados$ = merge(filtro$, this.dataRefresh$).pipe(
    tap(() => this.emChamadaRemota = true),
    switchMap(info => forkJoin(
        of(info),
        this._servicoPedidos.obtemPedidos(this._idInstancia,
                                info.paginaAtual,
                                info.elemsPagina,
                                info.estado)
    )),
    shareReplay(),
    tap(([info, pedidos]) => this.existemMais = pedidos.length === info.elemsPagina),
    scan((todosPedidos, info) => !info[0].addPedidosToExisting ?
        info[1] :
        todosPedidos.concat(info[1]), []),
    tap(() => this.emChamadaRemota = false),
    catchError(erro => {
        this.emChamadaRemota = false;
        this.trataErro(erro);
        this.existemMais = false;
        return of([]);
    })
);

Просто краткий обзор того, что я пытаюсь сделать здесь ... tap используется для установки и очистки поля, которое управляет счетчиком ожидания (emChamadaRemota), и для контроля, должна ли быть кнопка загрузки еще показано (existemMais). Я использую forkJoin внутри switchMap, потому что мне нужно получить доступ к информации о текущем поиске по конвейеру. scan есть, потому что загрузка большего количества элементов должна добавить элементы на предыдущую загруженную страницу.

Теперь я также использую перехватчик, который отвечает за установку правильных заголовков и обработку типичных ошибок (401, 503 и т. Д.) С помощью стратегии повторных попыток. Вот код для intercept метода:

intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    const headers = this.obtemHeaders();
    const requestClonado = req.clone({ headers });
    return next.handle(requestClonado).pipe(
                    retryWhen(this.retryStrategy()),
                    catchError(err => {
                        console.error(err);
                        let msgErro: string;
                        if(err instanceof HttpErrorResponse && this._servicoAutenticacao.trataErroFimSessao(err)) {
                            msgErro = "A sua sessão terminou. Vai ser redirecionado para a página de login" ;
                        }
                        else if(err.status === 503 ) {
                            msgErro = "O servidor não devolveu uma resposta válida (503).";
                        }
                        else {
                            msgErro = err.error && err.error.message ? err.error.message : "Ocorreu um erro no servidor.";
                        }
                        if(err.status !== 503) {
                            this._logger.adicionaInfoExcecao(msgErro).subscribe();
                        }
                        return throwError(msgErro);
                    }
                ));
} 

Теперь проблема: если я получаю ошибку при вызове веб-службы, все работает хорошо, но моя наблюдаемая будет «убита» ... и это имеет смысл, потому что операторы должны отловить ошибку и «отписаться» поток (по крайней мере, это то, что я понял из некоторых статей, которые я прочитал).

Я читал некоторые статьи, в которых говорится, что решение состоит в создании внутренней наблюдаемой, которая никогда не генерирует и которая оборачивает наблюдаемую информацию, возвращаемую вызовом веб-службы. Это путь? Если так, могу ли я сделать это на уровне перехватчика? Или, в случае ошибки, я должен просто перестроить свою наблюдаемую цепочку (но без автоматического запуска ее с помощью оператора startWith)?

1 Ответ

0 голосов
/ 31 октября 2018

Что ж, после некоторых тестов единственный способ, которым мне удалось заставить его работать (без устранения распространяющейся ошибки повторной попытки / перехвата), - это перестроить конвейер при возникновении исключения. Итак, я переместил код создания в метод:

private setupPipeline(runFirstTime = true) {
  const filtro$ = this.estadoPedido.valueChanges.pipe(
                   distinctUntilChanged(),
                   tap(_ => {
                         this._paginaAtual = 0;
                         this.existemMais = true;
                   }),
                   runFirstTime ? startWith(this.estadoPedido.value) : tap(),
                   map(estado => new DadosPesquisa(this._paginaAtual,
                                                  this._elemsPagina,
                                                  estado,
                                                  false))
                   );
  this.pedidosCarregados$ = merge(filtro$, this.dataRefresh$).pipe( 
       //same as before...
      catchError(erro => {
         this.emChamadaRemota = false;
         this.trataErro(erro);
         this.existemMais = false;
         setTimeout(() => this.setupRxjs(false), 100); // reset pipeline
         return of([]);
    })
}

Метод вызывается из метода init и внутри оператора catchError. Я уверен, что есть лучший способ, но воссоздание конвейера позволило мне использовать код почти как есть ...

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...