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

使用takeUntil时因先前派发的action导致Effect请求被立即取消的问题

问题描述

当用户选择订单时,订单UI组件会派发selectOrder action:

selectOrder(orderId): void {
  this.store$.dispatch(
    selectOrder({orderId})
  );
}

随后会加载所有相关数据:

private getOrderOnSelectOrder(): void {
  this.store$.select(selectSelectedOrder).pipe(filter((selectedOrder: Order | null): selectedOrder is Order => Boolean(selectedOrder)))
    .subscribe((selectedOrder: Order) => {
      this.store$.dispatch(getOrder({
        orderId: selectedOrder.id
      }))
    }
  );
}

一个Effect监听getOrder action以向服务器发起请求:

public getOrder$ = createEffect(() =>
        this.actions$.pipe(
            ofType(getOrder),
            switchMap((props: GetOrderProps) =>
                this.orderService.getOrder(props.orderId).pipe(
                    map((order: Order) =>
                        /* Logic for successfully received order */
                    ),
                    catchError((error: HttpErrorResponse) =>
                        /* Logic for failure */
                    ),
                    takeUntil(this.actions$.pipe(ofType(selectOrder))),
                ),
            ),
        ),
    );

添加takeUntil是为了在用户选择新订单时丢弃旧请求,但所有请求都被立即取消,无法获取订单数据。想知道该行为的原因——明明请求是在selectOrder action之后发起的,为何对应的Observable会受到影响?

问题原因与解决方案

核心原因

takeUntil的触发逻辑是:一旦监听的Observable(这里是actions$.pipe(ofType(selectOrder)))发出值,当前数据流就会立即终止。

你的流程里,selectOrder action派发后会同步触发getOrder action的派发,但Redux的action流是同步的——当Effect里的takeUntil开始监听时,它会“捕获”到刚刚派发的那同一个selectOrder action,直接触发终止逻辑,导致请求刚发起就被取消。

解决方案

有两种可行的处理方式:

  1. 跳过当前触发请求的selectOrder
    让takeUntil只监听当前请求发起之后的新selectOrder action,在ofType(selectOrder)后添加skip(1):
public getOrder$ = createEffect(() =>
        this.actions$.pipe(
            ofType(getOrder),
            switchMap((props: GetOrderProps) =>
                this.orderService.getOrder(props.orderId).pipe(
                    map((order: Order) =>
                        /* Logic for successfully received order */
                    ),
                    catchError((error: HttpErrorResponse) =>
                        /* Logic for failure */
                    ),
                    takeUntil(this.actions$.pipe(
                        ofType(selectOrder),
                        skip(1) // 跳过触发当前请求的那个selectOrder
                    )),
                ),
            ),
        ),
    );
  1. 直接利用switchMap的特性
    switchMap本身就具备“新请求进来时自动取消未完成的旧请求”的能力,你的需求其实已经被switchMap覆盖了,完全可以去掉takeUntil这一行:
public getOrder$ = createEffect(() =>
        this.actions$.pipe(
            ofType(getOrder),
            switchMap((props: GetOrderProps) =>
                this.orderService.getOrder(props.orderId).pipe(
                    map((order: Order) =>
                        /* Logic for successfully received order */
                    ),
                    catchError((error: HttpErrorResponse) =>
                        /* Logic for failure */
                    )
                ),
            ),
        ),
    );

内容的提问来源于stack exchange,提问作者Ilya Loskutov

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 15:37:10