Задержка RxJS не менее X миллисекунд

Я пытаюсь реализовать следующее поведение в RxJS:

  • Пожар события
  • Вызов http API
  • Когда API возвращается, либо:
    • Подождите, пока не пройдет X миллисекунд с момента запуска события
    • Немедленно возвращайтесь, если X миллисекунды уже прошли с момента запуска события

Это очень полезно для UX, потому что даже если вызов занимает 1 мс, я бы хотел показать значок загрузки не менее 100 мс.

Я не нашел способа реализовать это либо с delay, throttle, debounce, либо с его вариантами.

this.eventThatFires
    .switchMap(data => {
        let startTime = Date.now();
        return this.callHttpService(data)
            .delay(new Date(startTime + 1000));
    })

Я предположил, что что-то вроде этого сработало, но использование абсолютной даты, похоже, делает разницу во времени с текущим временем и не назначает задержку для этого абсолютного времени.


EDIT:

Кажется, нет встроенного оператора, который работает как то, что я описываю. Я просто создал его, потому что я буду использовать его во всем приложении:

import { Observable } from "rxjs/Observable";

function delayAtLeast<T>(this: Observable<T>, delay: number): Observable<T> {
    return Observable.combineLatest(
        Observable.timer(delay),
        this)
    .map(([_, i]) => i);
}

Observable.prototype.delayAtLeast = delayAtLeast;

declare module "rxjs/Observable" {
    interface Observable<T> {
        delayAtLeast: typeof delayAtLeast;
    }
}

Ответ 1

Эффективно delay по дате совпадает с delay по числу, единственное различие заключается в том, что продолжительность задержки вычисляется как разница в указанной дате и текущем времени.

Вы можете использовать оператор delayWhen для вычисления задержки при испускании значения:

this.eventThatFires
    .switchMap(data => {
        let startTime = Date.now();
        return this.callHttpService(data)
            .delayWhen(() => Rx.Observable.timer(500 + startTime - Date.now()))
    })

Ответ 2

Что не так с вашим решением combLatest?

Вы также можете использовать zip:

this.eventThatFires
    .switchMap(data => Observable.zip(
        this.profileService.updateInfo(profileInfo)),
        Observable.timer(500),
        x => x));

Ответ 3

Богдан Савлук ответит с задержкой по функциональности

let effectiveDelay=(delay)=>{
    let effectiveTimer = (now=>delay=>timer(delay+now - Date.now() )) (Date.now())
    return delayWhen(()=>effectiveTimer(delay));
}


this.eventThatFires
    .switchMap(data => this.callHttpService(data)
                .pipe(effectiveDelay(500)))

Случай http запроса (опрос не реже, чем каждые x секунд)

import { of, timer } from 'rxjs';
import { ajax } from 'rxjs/ajax';
import { delayWhen, switchMap, tap, repeat, map } from 'rxjs/operators';

const display = text => {
    console.log(new Date(), text);
};

let effectiveDelay=(delay)=>{
    let effectiveTimer = (now=>delay=>timer(delay+now - Date.now() )) (Date.now())
    return delayWhen(()=>effectiveTimer(delay));
}

of({}).pipe(
    switchMap(data => {
        return ajax({url:"https://httpbin.org/delay/2", crossDomain:true}).pipe(effectiveDelay(1000), map(ax=>ax.response) )
    }), tap(display), repeat())
  .subscribe()

Смотрите: онлайн