如何使用rxjs取消嵌套的请求

时间:2019-04-01 01:07:50

标签: angular rxjs rxjs6

buttonClicked$.pipe(
switchMap(value=>makeRequest1),
switchMap(responseOfRequest1=>makeRequest2))
.subscribe()

我需要依次发出2个http请求,如果单击按钮,则中止下游待处理的请求。上面的代码是正确的方法吗?我了解一次新点击将取消待处理的request1。如果点击发生在request2期间,request2也会被取消吗?

3 个答案:

答案 0 :(得分:1)

为什么第二个switchMap可能不会取消

switchMap运算符仅在从其上游Observable(即makeRequest1)接收到下一个事件时才取消其内部Observable,而不是在源Observable发出时(即buttonClicked$)取消其内部Observable。因此,请考虑以下事件顺序:

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

在这种情况下,因为在接收到对请求1的第二个响应之前就已经收到了对请求2的响应,所以即使在完成请求2之前第二次单击按钮,也不会取消请求2。

如果请求1在请求2收到响应之前发出其第二个响应,则请求2被取消。但是请注意,并不是单击按钮本身会取消它,而是发出请求1取消它。

解决方案

如果您希望在单击按钮时可靠地取消两个请求,则两个请求都必须在同一switchMap内进行:

buttonClicked$.pipe(
  switchMap(clickValue => makeRequest1(clickValue).pipe(
    mergeMap(request1Value => makeRequest2(request1Value)))
).subscribe(request2Value => /* ... */);

答案 1 :(得分:1)

这是正确的行为,因为switchMap仅在收到next通知时才从其内部Observable取消订阅。这意味着当您有两个switchMap时,第一个必须先发出才能触发第二个中的取消订阅。

您可以通过仅使用一个switchMap并将第二个makeRequest2调用与makeRequest1放在一个链中来避免这种情况。

buttonClicked$.pipe(
  switchMap(value => makeRequest1(value).pipe(
    mergeMap(responseOfRequest1 => makeRequest2(responseOfRequest1)),
  )),
  .subscribe()

通过这种方式,当buttonClicked$发出信号时,switchMap便从其内部Observable取消订阅,该内部Observable也取消了mergeMap的订阅,如果第二个呼叫仍未决,它将取消第二个呼叫。

答案 2 :(得分:0)

是的,它确实具有一定间隔。我没有通过网络请求对其进行测试。

每单击一次,第二个可观察间隔就会重新开始。

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>