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

Возьмем, к примеру:

 this.http.get('/getdata').pipe(delay(2000))

Я хочу, чтобы этот запрос занимал минимум 2 секунды, но не дольше, чем это требуется для выполнения запроса.

Другими словами:

  1. если для выполнения запроса требуется 1 с, я хочу, чтобы наблюдаемое завершилось за 2 с.

  2. если для выполнения запроса требуется 3 секунды, я хочу, чтобы наблюдаемое завершилось за 3 секунды, а НЕ 5 секунд.

Есть ли какой-нибудь другой канал, кроме delay(), который может достичь этого, о котором я не знаю, или есть способ создать для этого собственный канал, если это необходимо?

Вариант использования - показать загрузчик, однако, если запрос завершается слишком быстро, это не выглядит хорошо, когда загрузчик просто "мигает" на долю секунды.


person parliament    schedule 03.01.2019    source источник
comment
Рассмотрим лучшее решение с точки зрения UX. Показывать загрузчик только через короткое время, если запрос еще не вернулся. Это означает, что если запрос возвращается быстро, загрузчик не требуется.   -  person GregL    schedule 04.01.2019
comment
@GregL, который просто перемещает ту же проблему. Если запрос занимает немного больше времени, чем короткое время, загрузчик появляется, а затем немедленно исчезает.   -  person jonrsharpe    schedule 04.01.2019
comment
Почему бы не перенести эту логику в загрузчик? Он отображается, когда начинается запрос, затем получает команду идти, когда запрос заканчивается, и он должен решить, нужно ли ему делать паузу перед исчезновением, чтобы избежать эффекта мерцания. Кажется странным намеренно задерживать фактические данные и обработку, а не только отображение.   -  person jonrsharpe    schedule 04.01.2019
comment
@jonrsharpe У вас всегда будет случай, когда загрузчик отображается неудобно непосредственно перед возвратом ответа. Речь идет о снижении вероятности этого путем исключения счетчика для пользователей, когда ответ возвращается до того, как пользователь думает, что пользовательский интерфейс не отвечает (что является целью загрузчика, чтобы показать вам, что что-то происходит).   -  person GregL    schedule 04.01.2019
comment
Тем не менее, существуют допустимые варианты использования для преднамеренной задержки обновления пользовательского интерфейса, даже если ответ быстрый. Есть определенные операции, которые, как мы привыкли считать, занимают заметный период времени. Если вместо этого кажется, что они происходят мгновенно, люди не доверяют системе и считают, что она была слишком быстрой, чтобы быть реальной.   -  person GregL    schedule 04.01.2019


Ответы (2)


Чтобы ответить на заданный вопрос, вы можете просто использовать combineLatest() для объединения timer(2000) наблюдаемого и наблюдаемого запроса, а затем просто игнорировать результат наблюдаемого таймера. Это работает, потому что combineLatest ожидает, пока все наблюдаемые не выдадут хотя бы одно значение, прежде чем испускать одно значение.

combineLatest(this.http.get('/getdata'), timer(2000), x => x)
person GregL    schedule 03.01.2019
comment
спасибо GregL! Я использовал ваш пример и обновил его до последней версии rxjs. - person chriscrossweb; 29.05.2020
comment
это не сработает, если http-запрос вернет ошибку = ›наблюдаемое завершится незамедлительно. - person andreivictor; 16.06.2020

Благодаря GregL я обновил это, чтобы просто использовать forkJoin. Это получит последнее значение потоков. Но если вы хотите проверять его при каждой эмиссии, вы можете заменить forkJoin на combLatest, и это тоже сработает. В моем рабочем примере:

    this.ibanSubscription = forkJoin({
        iban: this.ibantobicService.getById(Iban),
        timer: timer(1000) //so now the ajax call will take at least 1 second 
        }
    ).pipe(
        map( (stream: any) => <BicModel>stream.iban),
        switchMap( (bic: BicModel) => of(this.processIbanData(bic))),
        catchError((error: any) => of(this.messageList.handleError(error))),
        finalize(() => this.loadIbanToBicFinalize())
   ).subscribe();
person chriscrossweb    schedule 29.05.2020