因此,我开始使用rxjs,并且遇到了有关从Web服务调用中获取错误后保持我的可观察内容存活的最佳方法的问题。
在显示代码之前,这是我当前的情况:一个角度组件,必须加载已分页的初始列表,并且还可以通过更改组合上的项目进行过滤。为了使用rxjs解决此问题,我考虑过合并两个可观察对象:一个处理选择的change事件,另一个用于加载更多项。这是我正在使用的代码:
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>();
这两个可观察对象合并在一起,这样我就可以使用一条路径来调用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([]);
})
);
快速回顾一下我要在此处执行的操作... 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);
}
));
}
现在,问题是:如果我在Web服务调用上遇到错误,一切正常,但是我的可观察对象将被“杀死” ...这是有道理的,因为操作员应捕获错误并“取消订阅”流(至少,这是我从阅读过的一些文章中了解的内容)。
我读过一些文章,说解决方案是创建一个内部的可观察对象,该对象从不抛出并且包装从Web服务调用返回的可观察对象。这是要走的路吗?如果是这样,我可以在拦截器级别执行此操作吗?还是在发生错误的情况下,我是否应该简单地重建我的可观察链(但不要使用startWith运算符自动启动它)?
答案 0 :(得分:0)
好吧,经过一些测试,我设法使其正常工作的唯一方法(不放弃我所拥有的重试/捕获错误传播)是在引发异常时重建管道。因此,我将创建代码移到了一个方法中:
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运算符内部调用的。我敢肯定有更好的方法,但是重新创建管道使我几乎可以按原样重用代码...