【问题标题】:switchMap and debounceTime cancel pendingswitchMap 和 debounceTime 取消挂起
【发布时间】:2020-06-24 05:23:32
【问题描述】:

我有一种方法可以根据搜索词搜索员工。

this._sub.pipe(
    debounceTime(500),
    filter(x => !!x),
    distinctUntilChanged(),
    switchMap(this.getResults.bind(this))
).subscribe((d: IDisplayEmp[]) => {
    console.log('RES', d)
    this.displayResults = d;
});

this._sub.next('mySearchTerm');

这很好用,如果发出新的 API 调用,则会取消以前的 API 调用。然而问题来了。

只有在 debounce 发出后才会取消活动的 API 调用。如果在 debounce 等待期间有一个活动的 API 调用返回,它仍然会触发我的订阅。

我理解这是因为取消只发生在 switchmap 的范围内。

是否有可能对此进行重构,以便在去抖动等待输入停止时取消任何待处理的 API 调用?

可能是一种幼稚的方法,但我尝试了类似下面的方法,它会发出值,但现在没有反跳效果。

this._sub.pipe(
    filter(x => !!x),
    distinctUntilChanged(),
    switchMap((x) => {
        return of(x)
            .pipe(
                debounceTime(500),
                this.getResults.bind(this)
            );
    })
).subscribe((d: IDisplayEmp[]) => {
    console.log('RES', d)
    this.displayResults = d;
});

感谢您的帮助。

【问题讨论】:

  • 我很不清楚你想要实现什么。也许throttleTime 做你想做的事?
  • @martin 我正在尝试去抖动输入然后进行 API 调用。如果有新输入进入去抖,将等待输入停止,然后再发出结果值。我希望在 debounce 等待时取消活动的 API 调用(如果有的话),而不是在它发出当前行为之后。感谢您对throttleTime的信息
  • 那么你应该concat你的api调用。 learnrxjs.io/operators/combination/concat.html
  • @ritaj 抱歉,我正在努力了解如何应用它,我只在 debounce 发出值时触发单个 API 调用?
  • 我会发布答案。

标签: angular typescript rxjs observable


【解决方案1】:

每当this._sub 发出时,使用takeUntil 取消您的请求。

this._sub.pipe(
    debounceTime(500),
    filter(x => !!x),
    distinctUntilChanged(),
    switchMap(searchTerm => this.getResults(searchTerm).pipe(
      takeUntil(this._sub)
    ))
).subscribe((d: IDisplayEmp[]) => {
    console.log('RES', d)
    this.displayResults = d;
});

【讨论】:

  • 谢谢,效果很好!查看文档和您的评论也很有意义。
【解决方案2】:

我相信这应该适合你:

使用concat,以便第二次发射等待第一次发射完成。

const getFromAPI = (key: string): Observable<number> => {
    return;
};

const keys: Observable<string> = new Observable();

concat(
    keys.pipe(
        debounceTime(500),
        switchMap(key => getFromAPI(key)),
        take(1)
    )
).subscribe(console.log);

另一个例子:

const getFromAPI = (key: string): Observable<number> => {
    return;
};

const keys: Observable<string> = new Observable();

const debouncedKeys = keys.pipe(debounceTime(500));

concat(
    debouncedKeys.pipe(
        switchMap(key => getFromAPI(key)),
        take(1),
        takeUntil(debouncedKeys)
    )
).subscribe(console.log);

【讨论】:

  • 啊,我现在明白了,感谢您为示例所做的努力。然而,这不是我之后的行为。 keys 将是用户在输入中输入的搜索词。每当发出新的搜索词时,我都需要它来取消先前的待处理请求,但仅在经过一段时间的去抖动后才发出新的 API。我认为这是一个更好的解释,我会更新 OP!
  • 哦,那我相信你只是想加takeUntil。我会编辑答案。
【解决方案3】:

接受的答案应该可以正常工作,但我想分享另一种使用timer 而不是debounceTime 的方法:

this._sub.pipe(
    filter(x => !!x),
    distinctUntilChanged(),
    switchMap(val => timer(500).pipe(
        switchMap(() => this.getResults(val))
    ))
).subscribe(...);

现在,如果排放量达到switchMaptimer 及其中的所有内容(包括活动的网络请求)都将被取消,并会创建一个新的timer

【讨论】:

  • 这实际上也比使用 takeUntil 短了一行,因为您不必使用 debounceTime 并且两者都需要在 switchMap 内使用内管。
  • 这一个必须是一般情况下接受的答案!说我没有主题变量,但是使用单行this.myCustomCombobox.filterChange.asObservable(),那么我不能使用takeUntil(或者必须引入一个变量),但是timer的解决方案仍然有效!
【解决方案4】:

这里不确定,需要探索,但你可以将 _sub 转换为两个 observable,一个会去抖动并返回原始查询,第二个不会去抖动并返回 null/undefined。

然后将它们合并并调用 switchMap。

未反弹的 observable 应该允许您取消调用,因为它也会到达 switchmap。在这种情况下, switchMap 可能会返回一个空的或永远不可观察的。只有当它是与值一起出现的 debounched observable 时,您才重新创建 API 调用。

类似的东西?

const debounced = this._sub.pipe(debounceTime(500), filter(x => !!x), distinctUntilChanged());
const undebounced = this._sub.pipe(map(_ => undefined));

merge(debounced, undebounced).pipe(
    switchMap(val => {
        if (val) {
            return this.getResults(val);
        } else {
            return never;
        }
    })
);

【讨论】:

  • 感谢您的时间和帮助,但是接受的答案解决了我的问题。
猜你喜欢
  • 2020-04-19
  • 1970-01-01
  • 2023-03-19
  • 1970-01-01
  • 1970-01-01
  • 2017-10-22
  • 2019-12-31
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多