代码之家  ›  专栏  ›  技术社区  ›  Luis Abreu

rxjs+angular:errors“杀死”web服务调用中的observatable after error

  •  1
  • Luis Abreu  · 技术社区  · 6 年前

    所以,我已经开始玩rxjs了,我有一个问题,关于在从web服务调用中得到一个错误之后,如何保持我的可观察状态的最佳方法。

    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))
    );
    

    每当select更改时,我都会重置全局页计数器( tap startWith 接线员。最后,我将当前状态转换为一个对象,该对象具有加载值所需的所有值。

    dataRefresh$ = new Subject<DadosPesquisa>();
    

    这两个可观察对象被合并,这样我就可以有一个单独的路径来调用我的web服务:

    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([]);
        })
    );
    

    简单回顾一下我想做的事。。。 水龙头 用于设置和清理控制等待微调器的字段( emChamadaRemota )以及控制是否应显示“加载更多”按钮( existemMais ). 我用的是 forkJoin switchMap 因为我需要通过管道获取当前搜索的信息。 scan

    现在,我还使用了一个拦截器,它负责设置正确的头并用重试策略处理典型错误(401503等)。这是密码 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);
                        }
                    ));
    } 
    

    我读过一些文章,其中提到解决方案是创建一个内部的observable,它从不抛出,并封装从web服务调用返回的observable。走这条路吗?如果是的话,我能在拦截器级别上做吗?或者,在发生错误的情况下,我是否应该简单地重建我的可观察链(但不使用startWith操作符自动启动)?

    1 回复  |  直到 6 年前
        1
  •  0
  •   Luis Abreu    6 年前

    好吧,在一些测试之后,我唯一能让它工作的方法(不放弃重试/捕获错误传播)就是在抛出异常时重建管道。因此,我将创建代码移到了一个方法中:

    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方法内部调用的,并从withint调用catchError运算符。我确信有更好的方法,但是重新创建管道允许我几乎按原样重用代码。。。