如何使用 rxjs 取消嵌套请求
how to cancel nested request with rxjs
buttonClicked$.pipe(
switchMap(value=>makeRequest1),
switchMap(responseOfRequest1=>makeRequest2))
.subscribe()
我需要按顺序发出 2 个 http 请求,并在单击按钮时中止下游挂起的请求。上面的代码是正确的方法吗?我了解新的点击将取消待定 request1
。如果点击发生在 request2
期间,request2
是否也会被取消?
为什么第二个 switchMap 可能无法取消
switchMap
运算符仅在从其上游 Observable 接收到下一个事件时(即 makeRequest1
)取消其内部 Observable,而不是在源 Observable 发出时(即 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
在这种情况下,因为在收到对请求 1 的第二次响应之前收到了对请求 2 的响应,即使第二次按钮单击发生在请求 2 完成之前,请求 2 也从未被取消。
如果请求 1 在请求 2 收到响应之前发出第二个响应,则请求 2 将被取消。但请注意,并不是按钮点击本身取消了它,而是请求 1 的发出取消了它。
解决方案
如果您希望在单击按钮时可靠地取消这两个请求,那么这两个请求需要在同一个 switchMap
:
buttonClicked$.pipe(
switchMap(clickValue => makeRequest1(clickValue).pipe(
mergeMap(request1Value => makeRequest2(request1Value)))
).subscribe(request2Value => /* ... */);
是的,它确实像间隔一样工作。我没有用网络请求测试它。
每点击一次,第二个可观察间隔就会重新开始。
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>
这是正确的行为,因为 switchMap
只有在收到 next
通知时才会取消订阅其内部 Observable。这意味着当您有两个 switchMap
时,第一个必须先发出才能触发第二个中的取消订阅。
您可以通过仅使用一个 switchMap
并将第二个 makeRequest2
调用放入具有 makeRequest1
.
的单个链中来避免这种情况
buttonClicked$.pipe(
switchMap(value => makeRequest1(value).pipe(
mergeMap(responseOfRequest1 => makeRequest2(responseOfRequest1)),
)),
.subscribe()
这样,当 buttonClicked$
发射时,它会让 switchMap
取消订阅其内部 Observable,内部 Observable 也会取消订阅 mergeMap
如果第二次调用仍在等待中,它将取消第二次调用。
buttonClicked$.pipe(
switchMap(value=>makeRequest1),
switchMap(responseOfRequest1=>makeRequest2))
.subscribe()
我需要按顺序发出 2 个 http 请求,并在单击按钮时中止下游挂起的请求。上面的代码是正确的方法吗?我了解新的点击将取消待定 request1
。如果点击发生在 request2
期间,request2
是否也会被取消?
为什么第二个 switchMap 可能无法取消
switchMap
运算符仅在从其上游 Observable 接收到下一个事件时(即 makeRequest1
)取消其内部 Observable,而不是在源 Observable 发出时(即 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
在这种情况下,因为在收到对请求 1 的第二次响应之前收到了对请求 2 的响应,即使第二次按钮单击发生在请求 2 完成之前,请求 2 也从未被取消。
如果请求 1 在请求 2 收到响应之前发出第二个响应,则请求 2 将被取消。但请注意,并不是按钮点击本身取消了它,而是请求 1 的发出取消了它。
解决方案
如果您希望在单击按钮时可靠地取消这两个请求,那么这两个请求需要在同一个 switchMap
:
buttonClicked$.pipe(
switchMap(clickValue => makeRequest1(clickValue).pipe(
mergeMap(request1Value => makeRequest2(request1Value)))
).subscribe(request2Value => /* ... */);
是的,它确实像间隔一样工作。我没有用网络请求测试它。
每点击一次,第二个可观察间隔就会重新开始。
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>
这是正确的行为,因为 switchMap
只有在收到 next
通知时才会取消订阅其内部 Observable。这意味着当您有两个 switchMap
时,第一个必须先发出才能触发第二个中的取消订阅。
您可以通过仅使用一个 switchMap
并将第二个 makeRequest2
调用放入具有 makeRequest1
.
buttonClicked$.pipe(
switchMap(value => makeRequest1(value).pipe(
mergeMap(responseOfRequest1 => makeRequest2(responseOfRequest1)),
)),
.subscribe()
这样,当 buttonClicked$
发射时,它会让 switchMap
取消订阅其内部 Observable,内部 Observable 也会取消订阅 mergeMap
如果第二次调用仍在等待中,它将取消第二次调用。