立即发送第一个项目,“debounce”以下项目

时间:2015-05-09 12:40:11

标签: rx-java rx-android

考虑以下用例:

  • 需要尽快交付第一件物品
  • 需要去抖以及1秒超时的事件

我最终实现了基于OperatorDebounceWithTime的自定义运算符,然后像这样使用它

.lift(new CustomOperatorDebounceWithTime<>(1, TimeUnit.SECONDS, Schedulers.computation()))

CustomOperatorDebounceWithTime立即发送第一个项目,然后使用OperatorDebounceWithTime运营商的逻辑去抖动以后的项目。

是否有更简单的方法来实现所描述的行为?让我们跳过compose运算符,它不能解决问题。我正在寻找一种方法来实现这一点,而无需实现自定义运算符。

10 个答案:

答案 0 :(得分:31)

更新:
从@ lopar的评论来看,更好的方法是:

Observable.from(items).publish(publishedItems -> publishedItems.limit(1).concatWith(publishedItems.skip(1).debounce(1, TimeUnit.SECONDS)))

这样的事情会起作用吗?

String[] items = {"one", "two", "three", "four", "five", "six", "seven", "eight"};
Observable<String> myObservable = Observable.from(items);
Observable.concat(myObservable.first(), myObservable.skip(1).debounce(1, TimeUnit.SECONDS))
    .subscribe(s -> System.out.println(s));

答案 1 :(得分:16)

@ LoortRaydenMK和@lopar的答案是最好的,但我想提出一些建议,以防它碰巧更适合你或类似情况的人。

有一个debounce()的变体,它带有一个函数来决定为此特定项目去抖多长时间。它通过返回在一段时间后完成的observable来指定它。您的函数可以为第一个项目返回empty(),为其余项目返回timer()。像(未经测试)的东西:

String[] items = {"one", "two", "three", "four", "five", "six"};
Observable.from(items)
    .debounce(item -> item.equals("one")
            ? Observable.empty()
            : Observable.timer(1, TimeUnit.SECONDS));

诀窍是这个函数必须知道哪个项目是第一个。你的序列可能知道。如果没有,您可能需要zip() range()或其他内容。在这种情况下,最好在另一个答案中使用该解决方案。

答案 2 :(得分:6)

使用RxJava 2.0的简单解决方案,从the answer for the same question for RxJS翻译而来,结合了throttleFirst和debounce,然后删除了重复项。

import matplotlib.pyplot as plt
import matplotlib.colors as mcolors
import numpy as np

clist = [(0, "red"), (0.125, "red"), (0.25, "orange"), (0.5, "green"), 
         (0.7, "green"), (0.75, "blue"), (1, "blue")]
rvb = mcolors.LinearSegmentedColormap.from_list("", clist)

N = 60
x = np.arange(N).astype(float)
y = np.random.uniform(0, 5, size=(N,))

plt.bar(x,y, color=rvb(x/N))
plt.show()

使用limit()或take()的方法似乎不能处理长期存在的数据流,我可能希望不断观察,但仍然会立即对第一个看到的事件采取行动。

答案 3 :(得分:6)

使用带有函数的debounce版本并以这种方式实现该函数:

    .debounce(new Func1<String, Observable<String>>() {
        private AtomicBoolean isFirstEmission = new AtomicBoolean(true);
        @Override
        public Observable<String> call(String s) {
             // note: standard debounce causes the first item to be
             // delayed by 1 second unnecessarily, this is a workaround
             if (isFirstEmission.getAndSet(false)) {
                 return Observable.just(s);
             } else {
                 return Observable.just(s).delay(1, TimeUnit.SECONDS);
             }
        }
    })

第一项立即发出。后续项目延迟一秒。如果延迟观察值在下一个项目到达之前未终止,则会被取消,因此可以实现预期的去抖动行为。

答案 4 :(得分:3)

LordRaydenMK and lopar's answer有问题:你总是失去第二项。我认为之前没有人发布这个,因为如果你有一个去抖动,你通常会有很多事件,而第二个就是去抖动。永远不会丢失任何事件的正确方法是:

observable
    .publish(published ->
        published
            .limit(1)
            .concatWith(published.debounce(1, TimeUnit.SECONDS)));

不用担心,你不会得到任何重复的事件。如果您不确定,可以运行此代码并自行检查:

Observable.just(1, 2, 3, 4)
    .publish(published ->
        published
            .limit(1)
            .concatWith(published))
    .subscribe(System.out::println);

答案 5 :(得分:0)

Ngrx-rxjs解决方案,将管道分成两个

onMyAction$ = this.actions$
    .pipe(ofType<any>(ActionTypes.MY_ACTION);

lastTime = new Date();

@Effect()
onMyActionWithAbort$ = this.onMyAction$
    .pipe(
        filter((data) => { 
          const result = new Date() - this.lastTime > 200; 
          this.lastTime = new Date(); 
          return result; 
        }),
        switchMap(this.DoTheJob.bind(this))
    );

@Effect()
onMyActionWithDebounce$ = this.onMyAction$
    .pipe(
        debounceTime(200),
        filter(this.preventDuplicateFilter.bind(this)),
        switchMap(this.DoTheJob.bind(this))
    );

答案 6 :(得分:0)

Kotlin扩展功能基于@lopar的注释:

fun <T> Flowable<T>.debounceImmediate(timeout: Long, unit: TimeUnit): Flowable<T> {
    return publish {
        it.take(1).concatWith(it.debounce(timeout, unit))
    }
}

fun <T> Observable<T>.debounceImmediate(timeout: Long, unit: TimeUnit): Observable<T> {
    return publish {
        it.take(1).concatWith(it.debounce(timeout, unit))
    }
}

答案 7 :(得分:0)

为防止进行双重订阅 使用这个:

    const debouncedSkipFirstStream$ = stream$.pipe(
        map((it, index) => ({ it, index })),
        debounce(({ index }) => (
            index ? new Promise(res => setTimeout(res, TimeUnit.SECONDS))
                : Rx.of(true))),
        map(({ it }) => it),
    );

如果使用拆分解决方案,您将看到两次“运行”打印

x = rxjs.Observable.create(o=>{
    console.info('run');
    o.next(1);
    o.next(2);
});
a = x.pipe(rxjs.operators.take(1));
b = x.pipe(rxjs.operators.skip(1), rxjs.operators.debounceTime(60));
rxjs.concat(a, b).subscribe(console.log);

答案 8 :(得分:0)

我对Dart的解决方案:

extension StreamExt<T> on Stream<T> {
  Stream<T> immediateDebounce(Duration duration) {
    var lastEmit = 0;
    return debounce((event) {
      if (_now - lastEmit < duration.inMilliseconds) {
        lastEmit = _now;
        return Stream.value(event).delay(duration);
      } else {
        lastEmit = _now;
        return Stream.value(event);
      }
    });
  }
}

int get _now =>  DateTime.now().millisecondsSinceEpoch;

答案 9 :(得分:0)

我去了

Flowable.concat(

    flowable // emits immediately
        .take(1)
        .skipWhile { it.isEmpty() },

    flowable // same flowable, but emits with delay and debounce
        .debounce(2, TimeUnit.SECONDS)
)
    .distinctUntilChanged()
相关问题