RxJS дроссельное поведение; немедленно получить первое значение
Пример Plunkr: https://plnkr.co/edit/NZwb3ol8CbZFtSc6Q9zm?p=preview
Я знаю, что для rxjs есть 3 метода дросселя (5.0 beta.4):
auditTime()
, throttleTime()
и debounceTime()
Я ищу поведение, которое lodash делает по умолчанию на газе:
- 1) Дайте мне первое значение немедленно!
- 2) для последовательных значений удерживайте значения для заданной задержки, а затем отправьте последнее полученное значение
- 3) когда задержка дросселя истекла, вернитесь в состояние (1)
Теоретически это должно выглядеть так:
inputObservable
.do(() => cancelPreviousRequest())
.throttleTime(500)
.subscribe((value) => doNextRequest(value))
Но
-
throttleTime
никогда не дает мне последнее значение, если оно генерируется в таймауте газа -
debounceTime
не срабатывает сразу -
auditTime
не срабатывает сразу
Могу ли я объединить любой из методов rxjs для достижения описанного поведения?
Ответы
Ответ 1
Я взял оператор auditTime и изменил 2 строки, чтобы добиться желаемого поведения.
Новый плункер: https://plnkr.co/edit/4NkXsOeJOSrLUP9WEtp0?p=preview
Оригинал:
Изменения:
from (auditTime):
protected _next(value: T): void {
this.value = value;
this.hasValue = true;
if (!this.throttled) {
this.add(this.throttled = this.scheduler.schedule(dispatchNext, this.duration, this));
}
}
clearThrottle(): void {
const { value, hasValue, throttled } = this;
if (throttled) {
this.remove(throttled);
this.throttled = null;
throttled.unsubscribe();
}
if (hasValue) {
this.value = null;
this.hasValue = false;
this.destination.next(value);
}
}
to (auditTimeImmediate):
protected _next(value: T): void {
this.value = value;
this.hasValue = true;
if (!this.throttled) {
// change 1:
this.clearThrottle();
}
}
clearThrottle(): void {
const { value, hasValue, throttled } = this;
if (throttled) {
this.remove(throttled);
this.throttled = null;
throttled.unsubscribe();
}
if (hasValue) {
this.value = null;
this.hasValue = false;
this.destination.next(value);
// change 2:
this.add(this.throttled = this.scheduler.schedule(dispatchNext, this.duration, this));
}
}
Итак, я запускаю таймаут после того, как значение было next
ed.
Использование:
inputObservable
.do(() => cancelPreviousRequest())
.auditTimeImmediate(500)
.subscribe((value) => doNextRequest(value))
Ответ 2
Для тех, кто ищет это после 2018 года: это было добавлено более года назад, но по какой-то причине документация еще не была обновлена.
RxJS коммит
Вы можете просто передать объект конфигурации в throttleTime
. По умолчанию используется { leading: true, trailing: false }
. Чтобы добиться поведения, обсуждаемого здесь, вы просто должны установить trailing
в true
: { leading: true, trailing: true }
РЕДАКТИРОВАТЬ:
Для полноты вот рабочий фрагмент:
import { asyncScheduler } from 'rxjs'
import { throttleTime } from 'rxjs/operators'
...
observable.pipe(
throttleTime(100, asyncScheduler, { leading: true, trailing: true })
)
Ответ 3
Для старых RxJs я написал оператор concatLatest
, который делает большую часть того, что вы хотите. С его помощью вы можете получить свое дросселирование с помощью этого кода:
const delay = Rx.Observable.empty().delay(500);
inputObservable
.map(value => Rx.Observable.of(value).concat(delay))
.concatLatest()
.subscribe(...);
Здесь оператор. Я принял удар по обновлению его для работы с RxJS5:
Rx.Observable.prototype.concatLatest = function () {
/// <summary>
/// Concatenates an observable sequence of observable sequences, skipping sequences that arrive while the current sequence is being observed.
/// If N new observables arrive while the current observable is being observed, the first N-1 new observables will be thrown
/// away and only the Nth will be observed.
/// </summary>
/// <returns type="Rx.Observable"></returns>
var source = this;
return Rx.Observable.create(function (observer) {
var latest,
isStopped,
isBusy,
outerSubscription,
innerSubscription,
subscriptions = new Rx.Subscription(function () {
if (outerSubscription) {
outerSubscription.unsubscribe();
}
if (innerSubscription) {
innerSubscription.unsubscribe();
}
}),
onError = observer.error.bind(observer),
onNext = observer.next.bind(observer),
innerOnComplete = function () {
var inner = latest;
if (inner) {
latest = undefined;
if (innerSubscription) {
innerSubscription.unsubscribe();
}
innerSubscription = inner.subscribe(onNext, onError, innerOnComplete);
}
else {
isBusy = false;
if (isStopped) {
observer.complete();
}
}
};
outerSubscription = source.subscribe(function (newInner) {
if (isBusy) {
latest = newInner;
}
else {
isBusy = true;
if (innerSubscription) {
innerSubscription.unsubscribe();
}
innerSubscription = newInner.subscribe(onNext, onError, innerOnComplete);
}
}, onError, function () {
isStopped = true;
if (!isBusy) {
observer.complete();
}
});
return subscriptions;
});
};
И здесь обновленный plunkr: https://plnkr.co/edit/DSVmSPRijJwj9msefjRi?p=preview
Примечание. Я обновил вашу версию lodash до последней версии. В lodash 4.7 я переписал операторы дроссельной заслонки /debounce, чтобы исправить некоторые ошибки в кромке. Вы использовали 4.6.1, у которого все еще были некоторые из этих ошибок, хотя я не думаю, что они влияют на ваш тест.