【问题标题】:RxJs: Pattern for observable search resultsRxJs:可观察搜索结果的模式
【发布时间】:2017-12-16 20:55:31
【问题描述】:

我的场景是一个带有搜索表单和结果列表的经典网页。我想将加载结果的行为封装在 Observable 中。

这是我目前在 TypeScript 中所做的:

function loadResults(query): Observable<T[]> {}

const results = new Subject<ResultEvent<T[]>>();

const results: Observable<ResultEvent<T[]>> =
  form.valueChanges
    .distinctUntilChanged()
    .do(() => results.next(ResultEvent.pending()))
    .switchMap(query => loadResults(query))
    .subscribe({
      next: (data: T[]) => results.next(ResultEvent.present(data)),
      error: err => results.next(ResultEvent.failed(err)),
    });

这个想法是results 始终包含搜索的当前状态:pendingpresentfailed。当查询发生变化时,结果设置为pending,当服务返回数据时,结果设置为present

我不喜欢这个解决方案的地方是对subscribe() 的显式调用。我宁愿有一个简单的Observable 可以订阅和取消订阅(例如在Angular 中使用async 管道),而无需创建显式订阅。 do 中的副作用看起来也很老套。

const results: Obserbable<ResultEvent<T[]>> = 
  form.valueChanges.distinctUntilChanged()
  . /* here be dragons */;

感谢您的任何建议和想法!

【问题讨论】:

    标签: rxjs reactivex


    【解决方案1】:

    我认为你想要一些类似的东西:

    const results$ = form.valueChanges
      // This is up to you, but with user input it might make sense to
      // give it just a little bit of time before we hit the server since
      // most user input will be more than a single character.
      //.debounceTime(100)
    
      .distinctUntilChanged()
    
      // Using switchMap guarantees that the inner observable will be
      // cancelled if the input changed while we are still waiting for
      // a result. Newer is always better!
      .switchMap(query => loadResults(query)
        // If we get data, we use it.
        .map(results => ResultEvent.present(results))
    
        // We catch errors and turn them into a failure event.
        .catch(err => Observable.of(ResultEvent.failed(err)))
    
        // Whatever happens, first things first.
        .startWith(ResultEvent.pending())
      );
    

    顺便说一句,我也会考虑在其中添加一个debounceTime

    这是一个 sn-p,您可以将其复制粘贴到 https://rxviz.com 以查看它的实际效果(不幸的是,他们的共享链接功能不再起作用)。确保将时间窗口设置为 10 秒。

    const ResultEvent = {
      pending: () => 'Pending',
      failed: err => 'Error: ' + err,
      present: data => 'Data: ' + data,
    };
    
    const loadResults = query => query === 2
      ? Rx.Observable.of(null).delay(500).switchMap(() => Rx.Observable.throw('Oops'))
      : Rx.Observable.of(42).delay(500)
    
    const input$ = Rx.Observable.timer(0, 2000).take(4);
    
    input$.switchMap(query => loadResults(query)
      .map(data => ResultEvent.present(data))
      .catch(err => Rx.Observable.of(ResultEvent.failed(err)))
      .startWith(ResultEvent.pending())
    )
    

    【讨论】:

    • 很好,非常感谢。这样看起来漂亮多了。关于去抖动的好处是,我故意将其省略以保持示例简单。
    • 当然可以。 :-)
    猜你喜欢
    • 2017-07-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-03-12
    • 2020-06-26
    相关资源
    最近更新 更多