【问题标题】:How to keep RxJs observable from completing on error?如何防止 RxJs 可观察到错误完成?
【发布时间】:2019-04-27 02:17:43
【问题描述】:

我有一个angular reactive form,它发出一个简单的 GET 请求。例如,如果我应该得到某种 HTTP 错误。可观察的todo$ 现在已完成我无法再更改我的搜索选项并单击以重试搜索。我创建了一个stackblitz demo,在演示中我在初始化程序中添加了我的订阅,如果流中出现错误,我会捕获它。在我抓住它之后,我再次调用初始化程序。这可行,但似乎是处理错误的错误方法。

我已经设置了表单,以便我可以取消以前的 HTTP 请求。

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>

【问题讨论】:

  • 你检查过retry吗?详情见这篇文章Error handling in RxJS
  • 我有,不是找请求立即重试,只是希望能改变输入的值,重新搜索。
  • 尝试将catchError 放在switchMap
  • @Kos 有效!谢谢,我以为我在错误的地方发现了错误。

标签: angular rxjs angular-reactive-forms


【解决方案1】:

Angular 的 Http 请求是不可变的。从客户端返回的 Observable 在您订阅时执行实际调用。您订阅多少次就会发出多少请求。这就是所谓的“Cold Observable”

如果你需要改变请求,你需要一个来自 HttpClient 的全新的 observable。

【讨论】:

  • 了解不可变部分,但我有我订阅的行为主题。那应该永远不会完成,但确实如此,当我再次单击搜索时,什么也没有发生。也许是我在哪里发现错误的原因?如果你注释掉第 45 和 46 行,你会发现现在点击搜索什么都不做。
  • 你不能注释掉 46 因为必须有 observable 返回。
  • 道歉意味着 44 和 45。
  • 但老实说,我不确定什么不起作用,因为您希望它起作用。如果我更改输入,则将新值传递给服务。正如预期的那样。那有什么问题呢?
  • 因为我用this.init();重新初始化订阅,如果你注释掉`this.todo$.next(''); this.init();` 它完成了 observable
【解决方案2】:

正如@kos 所说,我在错误的地方发现了错误。由于我切换到一个新的 observable,我应该在 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'
      });
    }))),
  );
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-02-20
    • 2020-06-24
    • 1970-01-01
    • 1970-01-01
    • 2018-07-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多