как отменить вложенный запрос с помощью rxjs

#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>