rxjs 可观察管道内的建模和 if/else 模式
modelling and if/else pattern inside an rxjs observable pipeline
我有一个史诗,我正在听一个动作流,当 'AUTH_STATUS_CHECKED'
的动作发生时切换到一个新的可观察 authState(app.auth())
。这个新的 observable 告诉我我是否通过了身份验证。
如果 auth 可观察到 returns 一个用户,则以该用户作为负载调度 'SIGNED_IN'
操作。
如果用户不存在,则取消承诺 app.auth().signInWithPopup(googleAuthProvider)
,当您收到响应时调度 'SIGNED_IN'
。
如果有任何错误,捕获它们并在控制台中告诉我。
基于上面的逻辑我实现了下面的管道
export const fetchAuthStatus = action$ =>
action$.pipe(
ofType('AUTH_STATUS_CHECKED'),
switchMap(() =>
authState(app.auth()).pipe(
map(user =>
user
? { type: 'SIGNED_IN', payload: user }
: app
.auth()
.signInWithPopup(googleAuthProvider)
.then(user => ({ type: 'SIGNED_IN', payload: user }))
),
catchError(error => console.log('problems signing in'))
)
)
);
它没有按预期工作。如果没有用户,promise 会被触发,但它不会等待响应,并且 dispatch 永远不会被触发。
我显然是 rxjs 的新手,我无法弄清楚这一点。我浏览了文档并尝试使用 tap() 和 mergeMap() 但我遇到了同样的错误。
如有任何指导,我们将不胜感激。
如果您需要等到某些内部 Observable/Promise 完成,您需要用例如包装它。 mergeMap
或 concatMap
。由于这两个运算符都要求您 return 和 Observable/Promise,因此您必须使用 of()
包装操作以将普通对象转换为 Observable:
import { of } from 'rxjs';
import { mergeMap } from 'rxjs/operators';
...
mergeMap(user => user
? of({ type: 'SIGNED_IN', payload: user })
: app.auth().signInWithPopup(googleAuthProvider)...
)
但是您的代码可以稍微优化并建议以更好的方式进行,您在这里缺少的是 map
期望一个普通对象,而 switchMap
期望一个 Promise
或 Observable
对象。
const signIn = (authProvider) => app.auth().signInWithPopup(googleAuthProvider)
.then(user => ({ type: 'SIGNED_IN', payload: user }));
export const fetchAuthStatus = action$ =>
action$.pipe(
ofType('AUTH_STATUS_CHECKED'),
switchMap(() => authState(app.auth())),
switchMap(user => user // <-- switchMap handle Promises or Observables
? of({ type: 'SIGNED_IN', payload: user }) // <-- returning an observable
: signIn(googleAuthProvider) // <-- returning a promise
),
catchError(error => console.log('problems signing in'))
);
备注:
- 不要嵌套管道符,尽量使用一个管道
- 提取Rx流外的复杂方法
- 使用
of
运算符将普通对象转换为 Observable
我有一个史诗,我正在听一个动作流,当 'AUTH_STATUS_CHECKED'
的动作发生时切换到一个新的可观察 authState(app.auth())
。这个新的 observable 告诉我我是否通过了身份验证。
如果 auth 可观察到 returns 一个用户,则以该用户作为负载调度 'SIGNED_IN'
操作。
如果用户不存在,则取消承诺 app.auth().signInWithPopup(googleAuthProvider)
,当您收到响应时调度 'SIGNED_IN'
。
如果有任何错误,捕获它们并在控制台中告诉我。
基于上面的逻辑我实现了下面的管道
export const fetchAuthStatus = action$ =>
action$.pipe(
ofType('AUTH_STATUS_CHECKED'),
switchMap(() =>
authState(app.auth()).pipe(
map(user =>
user
? { type: 'SIGNED_IN', payload: user }
: app
.auth()
.signInWithPopup(googleAuthProvider)
.then(user => ({ type: 'SIGNED_IN', payload: user }))
),
catchError(error => console.log('problems signing in'))
)
)
);
它没有按预期工作。如果没有用户,promise 会被触发,但它不会等待响应,并且 dispatch 永远不会被触发。
我显然是 rxjs 的新手,我无法弄清楚这一点。我浏览了文档并尝试使用 tap() 和 mergeMap() 但我遇到了同样的错误。
如有任何指导,我们将不胜感激。
如果您需要等到某些内部 Observable/Promise 完成,您需要用例如包装它。 mergeMap
或 concatMap
。由于这两个运算符都要求您 return 和 Observable/Promise,因此您必须使用 of()
包装操作以将普通对象转换为 Observable:
import { of } from 'rxjs';
import { mergeMap } from 'rxjs/operators';
...
mergeMap(user => user
? of({ type: 'SIGNED_IN', payload: user })
: app.auth().signInWithPopup(googleAuthProvider)...
)
但是您的代码可以稍微优化并建议以更好的方式进行,您在这里缺少的是 map
期望一个普通对象,而 switchMap
期望一个 Promise
或 Observable
对象。
const signIn = (authProvider) => app.auth().signInWithPopup(googleAuthProvider)
.then(user => ({ type: 'SIGNED_IN', payload: user }));
export const fetchAuthStatus = action$ =>
action$.pipe(
ofType('AUTH_STATUS_CHECKED'),
switchMap(() => authState(app.auth())),
switchMap(user => user // <-- switchMap handle Promises or Observables
? of({ type: 'SIGNED_IN', payload: user }) // <-- returning an observable
: signIn(googleAuthProvider) // <-- returning a promise
),
catchError(error => console.log('problems signing in'))
);
备注:
- 不要嵌套管道符,尽量使用一个管道
- 提取Rx流外的复杂方法
- 使用
of
运算符将普通对象转换为 Observable