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

如何避免@ngrx Effects中SwitchMap错误中断及后续请求执行

解决方案:终止失败后的请求链并避免Effect停止监听

你的问题核心有两个:一是认证注册失败后仍会执行后续的创建账户、登录请求,二是在Effect末尾加catch会导致后续操作无法触发。下面分步骤解决:

1. 终止失败后的后续请求流程

原来的代码中,authenticationService.SignUp的catch操作符把错误转换成了发射失败action的成功Observable,这会让后续的switchMap误以为流程正常,继续执行(甚至把失败action当成参数传给accountService.CreateAccount),完全不符合预期。

正确的思路是:在请求失败时,直接返回失败action,不进入后续的请求链。我们可以用mergeMap拆分成功/失败分支,只有成功时才继续执行后续请求:

@Effect() AccountSigningUp$ = this.actions$
  .ofType(ACCOUNT_SIGNING_UP)
  .map((action: AccountActions.AccountSigningUp) => action.payload)
  .switchMap(signUpModel => 
    // 第一步:执行认证提供商注册
    this.authenticationService.SignUp(signUpModel)
      // 注册成功时,才进入创建账户流程
      .mergeMap(signUpSuccessResult => {
        // 第二步:在API内创建账户
        return this.accountService.CreateAccount(signUpSuccessResult as SignUpModel)
          // 创建账户成功时,才进入登录流程
          .mergeMap(createAccountSuccessResult => {
            // 第三步:执行登录
            return this.authenticationService.Login(createAccountSuccessResult as LoginModel)
              .map(tokenModel => {
                this.tokenService.Save(tokenModel);
                return new AccountLoggedIn();
              })
              // 登录失败时,返回登录失败action,终止当前链
              .catch(loginErr => {
                const loginErrModel = new LoginModel();
                loginErrModel.message = loginErr;
                return Observable.of(new AccountLoginFailed(loginErrModel));
              });
          })
          // 创建账户失败时,返回注册失败action,终止当前链
          .catch(createAccountErr => {
            const signUpErrModel = new SignUpModel();
            signUpErrModel.message = createAccountErr;
            return Observable.of(new AccountSignUpFailed(signUpErrModel));
          });
      })
      // 认证注册失败时,直接返回失败action,完全终止后续所有流程
      .catch(signUpErr => {
        const signUpErrModel = new SignUpModel();
        signUpErrModel.message = signUpErr;
        return Observable.of(new AccountSignUpFailed(signUpErrModel));
      })
      // 注意:原代码末尾的.map(() => new AccountCreated())是逻辑错误!
      // 它会覆盖前面所有的action(包括登录成功/失败、注册失败等),请根据业务需求调整
  );

2. 避免Effect停止监听的问题

你提到在Effect末尾加catch会导致第二次触发失效,原因是:当Observable抛出错误时,外层的catch会返回一个新的Observable,但当这个Observable执行完成后,整个Effect的监听流就会终止(Effect需要持续监听action,不能提前完成)。

解决这个问题的关键是:不要把catch放在Effect的最外层,而是将catch嵌入每个独立的请求内部,这样每个请求的错误只会终止当前的请求分支,不会影响整个Effect对后续ACCOUNT_SIGNING_UP action的监听。

额外的逻辑修正

原代码末尾的.map(() => new AccountCreated())会把前面所有的action(包括AccountLoggedIn、AccountSignUpFailed等)都强制转换成AccountCreated,这显然不符合业务逻辑。如果需要在登录成功后同时发送AccountLoggedIn和AccountCreated,可以调整为:

.map(tokenModel => {
  this.tokenService.Save(tokenModel);
  // 同时发送两个action
  return Observable.concat(
    Observable.of(new AccountLoggedIn()),
    Observable.of(new AccountCreated())
  );
})

或者根据你的实际业务需求,调整action的发送时机。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 04:29:43