如何防止 RxJs 可观察到错误完成? [英] How to keep RxJs observable from completing on error?
问题描述
我有一个 angular 反应式表单,它发出一个简单的 GET 请求.例如,如果我应该收到某种 HTTP 错误.可观察的 todo$
现已完成,我无法再更改搜索选项并单击以重试搜索.我制作了一个 stackblitz 演示,在演示中我添加了我的初始化器内的订阅,如果流上有错误,我会捕获它.在我抓住它之后,我再次调用了初始化程序.这有效,但似乎是处理错误的错误方式.
我已经设置了表单,以便我可以取消之前的 HTTP 请求.
导出类 AppComponent 实现 OnDestroy {待办事项;加载 = 假;useFakeURL = false;todoSub$:订阅;todo$: BehaviorSubject <字符串 >= 新行为主题 <字符串 >('');@ViewChild('searchInput') searchInput: ElementRef;构造函数(私有提供者:ExampleService){this.init();}ngOnDestroy() {this.todoSub$.unsubscribe();}搜索() {const value = this.searchInput.nativeElement.value;this.todo$.next(value);this.useFakeURL = !this.useFakeURL;}私人初始化():无效{this.todoSub$ = this.todo$.pipe(过滤器(val => !!val),点按(() => {this.loading = true;}),switchMap(() => this.provider.query(this.todo$.getValue(), this.useFakeURL)),捕获错误(错误 => {this.todo$.next('');this.init();返回([]);}),).订阅(待办事项 =>{this.loading = false;this.todos = 待办事项;},错误 =>{this.loading = false;this.todos = 错误;});}}导出类 ExampleService {构造函数(私有 http:HttpClient){}查询(待办事项,useFakeURL:布尔值){如果(待办事项 === '所有'){待办事项 = '';}const url = useFakeURL ?'便便':`https://jsonplaceholder.typicode.com/todos/${todo}`;返回 this.http.get(url);}}
<input #searchInput type="text" placeholder="输入数字或输入'all'以获取所有待办事项"><button (click)="search()">获取待办事项</button><ul *ngIf="!loading && todos && todos.length"><li *ngFor="let todo of todos"><预>{{待办事项 |json}}
<pre *ngIf="!loading && todos && !todos.length">{{待办事项 |json}}
<div *ngIf="loading" class="loader">...加载</div>
正如@kos 所提到的,我在错误的地方发现了错误.由于我切换到一个新的 observable,我应该在 switchMap
中捕获错误.更新演示
search(term: Observable < string > ) {返回 term.pipe(过滤器(val => !!val),distinctUntilChanged(),去抖动时间(500),//将 catchError 附加到这里的内部可观察对象.switchMap(term => this.searchTodo(term).pipe(catchError(() => {返回({错误:未找到待办事项"});}))),);}
I have an angular reactive form that makes a simple GET request. If for example, I should get some sort of HTTP error. The observable todo$
has now completed I can no longer change my search options and click to retry searching. I've made a stackblitz demo, in the demo I've added my subscription inside an initializer and if there is an error on the stream I catch it. After I catch it, I call the initializer again. This works but seems like the wrong way to handle errors.
I've set up the form so that I can cancel previous HTTP requests.
export class AppComponent implements OnDestroy {
todos;
loading = false;
useFakeURL = false;
todoSub$: Subscription;
todo$: BehaviorSubject < string > = new BehaviorSubject < string > ('');
@ViewChild('searchInput') searchInput: ElementRef;
constructor(private provider: ExampleService) {
this.init();
}
ngOnDestroy() {
this.todoSub$.unsubscribe();
}
search() {
const value = this.searchInput.nativeElement.value;
this.todo$.next(value);
this.useFakeURL = !this.useFakeURL;
}
private init(): void {
this.todoSub$ = this.todo$.pipe(
filter(val => !!val),
tap(() => {
this.loading = true;
}),
switchMap(() => this.provider.query(this.todo$.getValue(), this.useFakeURL)),
catchError(error => {
this.todo$.next('');
this.init();
return of([]);
}),
)
.subscribe(
todos => {
this.loading = false;
this.todos = todos;
},
err => {
this.loading = false;
this.todos = err;
}
);
}
}
export class ExampleService {
constructor(private http: HttpClient) {}
query(todo, useFakeURL: boolean) {
if (todo === 'all') {
todo = '';
}
const url = useFakeURL ? 'poop' : `https://jsonplaceholder.typicode.com/todos/${todo}`;
return this.http.get(url);
}
}
<div class="container">
<input #searchInput type="text" placeholder="Enter a number or enter 'all' to get all todos">
<button (click)="search()">Get Todos</button>
</div>
<ul *ngIf="!loading && todos && todos.length">
<li *ngFor="let todo of todos">
<pre>
{{todo | json}}
</pre>
</li>
</ul>
<pre *ngIf="!loading && todos && !todos.length">
{{todos | json}}
</pre>
<div *ngIf="loading" class="loader">... LOADING</div>
As mentioned by @kos I was catching the error in the wrong place. Since I switched to a new observable I should be catching the error inside switchMap
. UPDATED-DEMO
search(term: Observable < string > ) {
return term.pipe(
filter(val => !!val),
distinctUntilChanged(),
debounceTime(500),
// attached catchError to the inner observable here.
switchMap(term => this.searchTodo(term).pipe(catchError(() => {
return of({
error: 'no todo found'
});
}))),
);
}
这篇关于如何防止 RxJs 可观察到错误完成?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!