switchMap 到 keep/cancel 请求 angular http 客户端
switchMap to keep/cancel request with angular http client
我有一些类似于
的ngrx效果
effect1 = this.actions$.pipe(
ofType('x'),
switchMap(data => this.httpClient.post(data)),
map(result => new mappedAction(result))
);
对于effect1,当收到新的trigger时,我想忘记并取消之前的请求,取消吧。我读了一些关于它的内容,似乎这就是 httpClient 请求上的 switchMap 所做的事情。
另一个效果2,我们假设,当接收到一个新的触发器时,我不关心上一个请求的结果,但我关心请求是否完成。我应该用什么代替 switchMap?
在那种情况下你应该使用 mergeMap
:
effect2 = this.actions$.pipe(
ofType('x'),
// inner subscription is not completed on source emition (many subscriptions allowed)
// but the order in which they complete is not mantained
mergeMap(data => this.httpClient.post(data)),
map(result => new mappedAction(result))
);
如果在您的情况下,顺序很重要,您应该使用 concatMap
。
注意使用 switchMap
进行服务器端变更。实际上,由于 post 请求,您似乎正在对某些资源进行服务器端变更。您需要确保即使请求在客户端被取消,它仍然由后端处理。
在我看来,对于 effect2,您有 2 个选择。
ConcatMap
ConcatMap 将触发器作为输入参数并将执行您的 http 请求。如果在 http 请求仍在进行时有新触发器,则此触发器将被缓冲,直到内部可观察对象 (http) 完成。根据您的情况,这可能会导致内存溢出。例如,请求需要 10 秒才能完成,但此效果每 100 毫秒触发一次。所有这些触发器都将被缓存并顺序执行。
MergeMap
MergeMap 未缓存触发器。它只是在触发动作后立即订阅内部可观察对象。但是内部可观察对象可能需要不同的时间,例如第一个 http 请求需要 20 秒,第二个 http 请求只需要 1 秒。在这种情况下,第二个 http 请求的操作员链中的进一步步骤将在第一个 http 请求完成之前执行。如果执行顺序对您很重要,这可能是个问题。
请注意,所有这些运算符都会发出 http 响应作为结果。如果您想在 http 请求完成时转发触发值,您可能会使用类似的东西
this.action().pipe(
concatMap(x =>
forkJoin([of(x), this.http(...)]).pipe(
map(([x]) => x)
)
)
);
抱歉缩进不好,我希望它有用,它实际上没有测试和写在我的 phone 上。当代码片段有任何问题时,请告诉我,我可以稍后改进它们。
您可以实现一个自定义运算符,其工作方式与 mergeMap
类似,但仅发出您映射到的最后一个 Observable 的项目。
function mergeMapLast<T, R>(next: ((data: T) => Observable<R>)): OperatorFunction<T, R> {
return (source: Observable<T>) => defer(() => {
let curr = 0;
return source.pipe(
mergeMap((data, index) => {
curr = index;
return next(data).pipe(filter(_ => index === curr));
})
);
});
}
用法
effect2 = this.actions$.pipe(
ofType('x'),
mergeMapLast(data => this.httpClient.post(data)),
map(result => new mappedAction(result))
);
我有一些类似于
的ngrx效果effect1 = this.actions$.pipe(
ofType('x'),
switchMap(data => this.httpClient.post(data)),
map(result => new mappedAction(result))
);
对于effect1,当收到新的trigger时,我想忘记并取消之前的请求,取消吧。我读了一些关于它的内容,似乎这就是 httpClient 请求上的 switchMap 所做的事情。
另一个效果2,我们假设,当接收到一个新的触发器时,我不关心上一个请求的结果,但我关心请求是否完成。我应该用什么代替 switchMap?
在那种情况下你应该使用 mergeMap
:
effect2 = this.actions$.pipe(
ofType('x'),
// inner subscription is not completed on source emition (many subscriptions allowed)
// but the order in which they complete is not mantained
mergeMap(data => this.httpClient.post(data)),
map(result => new mappedAction(result))
);
如果在您的情况下,顺序很重要,您应该使用 concatMap
。
注意使用 switchMap
进行服务器端变更。实际上,由于 post 请求,您似乎正在对某些资源进行服务器端变更。您需要确保即使请求在客户端被取消,它仍然由后端处理。
在我看来,对于 effect2,您有 2 个选择。
ConcatMap
ConcatMap 将触发器作为输入参数并将执行您的 http 请求。如果在 http 请求仍在进行时有新触发器,则此触发器将被缓冲,直到内部可观察对象 (http) 完成。根据您的情况,这可能会导致内存溢出。例如,请求需要 10 秒才能完成,但此效果每 100 毫秒触发一次。所有这些触发器都将被缓存并顺序执行。
MergeMap
MergeMap 未缓存触发器。它只是在触发动作后立即订阅内部可观察对象。但是内部可观察对象可能需要不同的时间,例如第一个 http 请求需要 20 秒,第二个 http 请求只需要 1 秒。在这种情况下,第二个 http 请求的操作员链中的进一步步骤将在第一个 http 请求完成之前执行。如果执行顺序对您很重要,这可能是个问题。
请注意,所有这些运算符都会发出 http 响应作为结果。如果您想在 http 请求完成时转发触发值,您可能会使用类似的东西
this.action().pipe(
concatMap(x =>
forkJoin([of(x), this.http(...)]).pipe(
map(([x]) => x)
)
)
);
抱歉缩进不好,我希望它有用,它实际上没有测试和写在我的 phone 上。当代码片段有任何问题时,请告诉我,我可以稍后改进它们。
您可以实现一个自定义运算符,其工作方式与 mergeMap
类似,但仅发出您映射到的最后一个 Observable 的项目。
function mergeMapLast<T, R>(next: ((data: T) => Observable<R>)): OperatorFunction<T, R> {
return (source: Observable<T>) => defer(() => {
let curr = 0;
return source.pipe(
mergeMap((data, index) => {
curr = index;
return next(data).pipe(filter(_ => index === curr));
})
);
});
}
用法
effect2 = this.actions$.pipe(
ofType('x'),
mergeMapLast(data => this.httpClient.post(data)),
map(result => new mappedAction(result))
);