RxJS Observable管道if/else逻辑问题:Epic未触发SIGNED_IN动作
问题分析与解决方案
你的核心问题在于**map操作符无法处理异步的Promise值**——当你在map里返回signInWithPopup的Promise时,map只会把这个Promise当作一个普通的返回值,不会等待它resolve,自然也就不会触发后续的动作派发。另外你的catchError也有问题,它需要返回一个Observable,否则整个流会被中断。
下面是修正后的代码,我会逐点解释修改逻辑:
import { of, empty, from } from 'rxjs'; import { switchMap, mergeMap, catchError } from 'rxjs/operators'; export const fetchAuthStatus = action$ => action$.pipe( ofType('AUTH_STATUS_CHECKED'), switchMap(() => authState(app.auth()).pipe( // 用mergeMap替代map,因为我们需要处理异步操作(Promise) mergeMap(user => { if (user) { // 有用户时,直接用of包装动作返回Observable return of({ type: 'SIGNED_IN', payload: user }); } else { // 无用户时,把Promise转换成Observable,等待它完成后映射动作 return from(app.auth().signInWithPopup(googleAuthProvider)).pipe( map(authResult => ({ type: 'SIGNED_IN', payload: authResult.user })) // 注意:signInWithPopup返回的是AuthResult,用户信息在authResult.user中 ); } }), // catchError必须返回一个Observable,否则流会终止 catchError(error => { console.log('problems signing in', error); // 这里可以返回empty()让流继续,或者返回一个错误动作 return empty(); }) )) );
关键修改点:
- 用
mergeMap替代map:mergeMap(或switchMap)能够处理返回Observable/Promise的函数,它会自动订阅Promise并等待其完成,把resolve的值传递下去。而map仅做同步的数值转换,不会处理异步值。 - 用
from()包装Promise:RxJS的from()操作符可以把Promise转换成Observable,这样就能在管道里正常处理异步结果了。 - 修正
catchError的返回值:catchError必须返回一个Observable,这里用empty()表示不派发任何动作,你也可以返回of({ type: 'SIGN_IN_FAILED', payload: error })来派发错误动作,方便后续状态处理。 - 修正
signInWithPopup的取值:Firebase的signInWithPopup返回的是AuthResult对象,用户信息嵌套在authResult.user中,原代码直接用user可能会导致取值错误。
如果你用的是NgRx,确保你的Epic符合Epic的类型要求:返回的是Observable<Action>,这样才能被store正确处理。
内容的提问来源于stack exchange,提问作者Josh Pittman
相关产品推荐
相关产品推荐

