#angular #rxjs #rxjs6
#angular #rxjs #rxjs6
Вопрос:
buttonClicked$.pipe(
switchMap(value=>makeRequest1),
switchMap(responseOfRequest1=>makeRequest2))
.subscribe()
Мне нужно последовательно выполнить 2 http-запроса и прервать ожидающий запрос ниже по потоку, если кнопка нажата. Является ли приведенный выше код правильным подходом? Я понимаю, что новый щелчок отменит ожидание request1
. если щелчок происходит во время request2
, request2
также отменяется?
Ответ №1:
Почему вторая карта переключения может не быть отменена
switchMap
Оператор отменяет свой внутренний наблюдаемый объект только тогда, когда он получает следующее событие от своего вышестоящего наблюдаемого объекта (т.Е. makeRequest1
), а не при отправке исходного наблюдаемого объекта (т.Е. buttonClicked$
). Итак, рассмотрим следующую последовательность событий:
first button click
first request 1 sent
first response to request 1 received
first request 2 sent
second button click
second request 1 sent
first response to request 2 received
second response to request 1 received
В этом сценарии, поскольку ответ на запрос 2 получен до получения второго ответа на запрос 1, даже несмотря на то, что второе нажатие кнопки произошло до завершения запроса 2, запрос 2 никогда не был отменен.
Если запрос 1 отправляет свой второй ответ до того, как запрос 2 получит ответ, то да, запрос 2 отменяется. Но обратите внимание, что само по себе его отменяет не нажатие кнопки, а отправление запроса 1.
Решение
Если вы хотите надежно отменить оба запроса при нажатии кнопки, то оба запроса должны быть выполнены в пределах одного и того же switchMap
:
buttonClicked$.pipe(
switchMap(clickValue => makeRequest1(clickValue).pipe(
mergeMap(request1Value => makeRequest2(request1Value)))
).subscribe(request2Value => /* ... */);
Ответ №2:
Это правильное поведение, потому что switchMap
отписывается от своего внутреннего Observable только тогда, когда получает next
уведомление. Это означает, что когда у вас есть два switchMap
запроса, то первый должен быть отправлен первым, чтобы инициировать отмену подписки во втором.
Вы можете избежать этого, используя только один switchMap
и поместив второй makeRequest2
вызов в единую цепочку с makeRequest1
.
buttonClicked$.pipe(
switchMap(value => makeRequest1(value).pipe(
mergeMap(responseOfRequest1 => makeRequest2(responseOfRequest1)),
)),
.subscribe()
Таким образом, при buttonClicked$
отправке он заставляет switchMap
отписаться от своего внутреннего Observable, который также внутренне отписывается mergeMap
, что отменит второй вызов, если он все еще находится в ожидании.
Ответ №3:
Да, это действительно работает так с интервалом. Я не тестировал это с сетевым запросом.
При каждом нажатии перезапускается второй наблюдаемый интервал.
const { fromEvent, interval, of } = rxjs;
const { switchMap } = rxjs.operators;
const btn = document.getElementById('btn');
const result = document.getElementById('result');
const click$ = fromEvent(btn, 'click');
click$
.pipe(
switchMap(value => of('Hello')),
switchMap(value => interval(1000))
)
.subscribe(
val => result.innerText = val
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/6.4.0/rxjs.umd.min.js"></script>
<button id="btn">Click Me</button>
<div id="result"></div>