You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用NgRx Effect触发多次后端调用时请求被取消的问题

问题分析与解决方案

你遇到的前12次请求被取消的核心原因,是loadItemCollectionWithSkip$ Effect中使用了switchMap操作符。

switchMap的特性是:每当新的Observable(这里对应新的loadItemCollectionWithSkip动作)进入时,会自动取消之前仍在执行的Observable。你一次性派发了13个请求动作,switchMap只会保留最后一个请求的Observable,前面12个请求因此被强制中断。

下面是几种针对性的解决方案:

方案1:串行执行请求(替换为concatMap)

如果不需要并发,想保持类似之前递归的串行执行逻辑,把switchMap换成concatMap即可。它会等待前一个请求完成后,再处理下一个动作对应的请求,不会取消任何请求:

loadItemCollectionWithSkip$ = createEffect(() =>
    this.actions$.pipe(
        ofType(this.loadActionGroup.loadItemCollectionWithSkip),
        concatMap((action) => { // 替换switchMap为concatMap
            return this.loadService.getListOdata(this.filterValue, action.skip).pipe(
                map((items: T[]) =>{
                    return this.loadActionGroup.loadItemSuccess({ items })
                })
            );
        })
    )
);

方案2:并行执行并控制并发数(替换为mergeMap)

如果想提升速度,同时避免触发API的并发限制,可以用mergeMap并指定并发数。比如同时最多执行3个请求:

loadItemCollectionWithSkip$ = createEffect(() =>
    this.actions$.pipe(
        ofType(this.loadActionGroup.loadItemCollectionWithSkip),
        mergeMap((action) => { // 替换switchMap为mergeMap
            return this.loadService.getListOdata(this.filterValue, action.skip).pipe(
                map((items: T[]) =>{
                    return this.loadActionGroup.loadItemSuccess({ items })
                })
            );
        }, 3) // 第二个参数控制并发数,可根据API限制调整
    )
);

方案3:直接批量发起请求(用forkJoin)

可以在获取总条数后,直接生成所有请求并批量执行,无需派发多个动作,逻辑更集中可控:

loadCollectionCount$ = createEffect(() =>
    this.actions$.pipe(
        ofType(this.loadActionGroup.loadItemCollection),
        withLatestFrom(this.store.select(this.loadedStatus)),
        switchMap(([{ }, loaded]) => {
            if (!loaded) {
                return this.loadService.getListCount(this.filterValue).pipe(
                    switchMap((result: number) => {
                        // 生成所有skip对应的请求Observable
                        const requests = [];
                        for (let i = 0; i < result; i += 500) {
                            requests.push(
                                this.loadService.getListOdata(this.filterValue, i).pipe(
                                    // 单个请求失败时返回空数组,避免影响整个批量请求
                                    catchError(() => of([]))
                                )
                            );
                        }
                        // 并行执行所有请求,等待全部完成后合并结果
                        return forkJoin(requests).pipe(
                            tap(() => this.store.dispatch(
                                this.loadActionGroup.loadItemCollectionCountSuccess({ count: result })
                            )),
                            map((allItems) => {
                                const mergedItems = allItems.flat();
                                return this.loadActionGroup.loadItemSuccess({ items: mergedItems });
                            }),
                            catchError(error => {
                                console.log(error, `${this.typeName} effects - loadCollection`);
                                return of(this.loadActionGroup.loadItemFailure({ error: { ...error } }))
                            })
                        );
                    }),
                    catchError(error => {
                        console.log(error, `${this.typeName} effects - loadCollection`);
                        return of(this.loadActionGroup.loadItemFailure({ error: { ...error } }))
                    })
                )
            } else {
                return of();
            }
        })
    )
);

如果需要串行执行批量请求,把forkJoin换成concat即可。

内容的提问来源于stack exchange,提问作者James D

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 20:40:21