RXjs问题:如何等待监听同一Action流的另一个Epic执行完成?
我来帮你拆解下当前遇到的问题,然后给出针对性的解决方案。
首先看你代码里的核心问题:你在第二个switchMap里用action$.ofType([GET_ACCOUNT_INFO_SUCCESS, GET_ACCOUNT_INFO_FAILURE])监听账户信息请求的结果,但紧接着用startWith(actions.getAccountInfo())分发动作。这里有两个关键漏洞:
操作符顺序导致动作丢失:
startWith是在do和ofType之后链式调用的,这意味着getAccountInfo()这个动作不会被ofType过滤(它的type不在你指定的列表里),也不会触发后面的do回调。更重要的是,这个动作分发后,对应的Epic同步处理并发出GET_ACCOUNT_INFO_FAILURE时,你当前的Observable可能还没完成对action$的订阅,直接错过了这个事件。同步动作的订阅时机问题:Redux的动作分发是同步的,当你用
startWith同步分发getAccountInfo()后,失败动作会立刻被发出,但此时action$.ofType(...)的订阅还没生效,自然捕获不到这个事件。
解决方案1:调整Observable结构,确保先分发动作再监听结果
你可以用concat和mergeMap组合,先分发getAccountInfo()动作,再监听后续的成功/失败事件,这样能保证订阅时机完全匹配:
export const rehydrateEpic = (action$: ActionsObservable) => action$.ofType(REHYDRATE).pipe( switchMap(() => action$.ofType(GET_JWT_SUCCESS).take(1).pipe( switchMap(() => // 先分发获取账户信息的动作,再监听结果 concat( of(actions.getAccountInfo()), action$.ofType([actions.GET_ACCOUNT_INFO_SUCCESS, actions.GET_ACCOUNT_INFO_FAILURE]).take(1) ).pipe( // 过滤掉我们主动分发的getAccountInfo动作,只处理结果 filter(action => action.type === actions.GET_ACCOUNT_INFO_SUCCESS || action.type === actions.GET_ACCOUNT_INFO_FAILURE ), do(action => { console.log(action); // 现在应该能正常捕获到失败动作了! }) ) ) ) ) );
解决方案2:拆分Epic,用动作触发实现顺序执行(更符合最佳实践)
既然你本来就想让两个Epic按顺序执行,不如直接拆分它们,让第二个Epic监听第一个Epic完成的动作(GET_JWT_SUCCESS),而不是都监听REHYDRATE,这样逻辑更清晰,也从根源上避免了订阅时机的问题:
// 第一个Epic:处理REHYDRATE并获取JWT export const rehydrateAndFetchJwtEpic = (action$: ActionsObservable) => action$.ofType(REHYDRATE).pipe( switchMap(() => // 这里是你的获取JWT的业务逻辑,最后分发GET_JWT_SUCCESS fetchJwtApi().pipe( map(() => actions.getJwtSuccess()), catchError(() => of(actions.getJwtFailure())) ) ) ); // 第二个Epic:等待JWT成功后,再执行账户信息获取逻辑 export const fetchAccountInfoAfterJwtEpic = (action$: ActionsObservable) => action$.ofType(GET_JWT_SUCCESS).pipe( switchMap(() => concat( of(actions.getAccountInfo()), action$.ofType([actions.GET_ACCOUNT_INFO_SUCCESS, actions.GET_ACCOUNT_INFO_FAILURE]).take(1) ).pipe( filter(action => action.type === actions.GET_ACCOUNT_INFO_SUCCESS || action.type === actions.GET_ACCOUNT_INFO_FAILURE ), do(action => { console.log(action); }) ) ) );
这样拆分后,两个Epic的职责更明确,也天然保证了执行顺序——只有第一个Epic完成并发出GET_JWT_SUCCESS后,第二个Epic才会触发。
内容的提问来源于stack exchange,提问作者robinvdvleuten

