我是可观察对象的新手,我很难理解如何正确使用它们。
假设我有这项服务:
class MyService {
query() {
return from(myApiCall()).pipe(
pluck('data'),
mergeMap(apiResponse => {
const obj = apiResponse.foo.map(el => {
return {
bar: el.bar,
otherProp: el.baz,
// how do I get the litteral value here and not an observable?
builtProp: this.buildProp(el.myProp)
}
})
return obj
)
}
buildProp(value) {
if(!value) {
return throwError('value missing')
}
return of(`the value is: ${value}`)
}
}
buildProp
是一个公共方法,我希望可以选择从MyService
类之外的其他地方调用它。它的作用没有异步,所以它最好直接返回值而不是可观察值。但是如果MyService
的某些方法返回可观察对象而其他方法不返回,那么使用它可能会不一致且令人讨厌。
我想过在构建对象时使用一个返回普通字符串的私有方法,以及另一个返回of(buildProp(val))
的公共方法,但这感觉很笨拙(不要介意现在为这两个独立的方法找到好名字的困难)。
Angular服务上的任何公共方法是否应该始终返回可观察值,即使该方法的作用没有异步?
在那种情况下,如何以优雅而惯用的Angular方式在返回的对象中获取文字字符串而不是builtProp
的可观察对象?
我喜欢在Angular中考虑服务的方式是,它们是放置任何类型业务逻辑的地方,从复杂的异步内容到简单的数据同步转换。
为什么要将逻辑放在服务中?一个很好的理由是,这使得代码更容易测试,并迫使您只保留与可视化和管理用户交互严格相关的组件。
话虽如此,没有理由认为服务必须返回可观察对象。它们必须返回有意义的返回:异步逻辑情况下的可观察对象,其他任何适合其他同步情况的东西。
这里有一些关于你的具体案例的思考。
首先,我质疑为什么要使用mergeMap?您应该可以使用map
来实现相同的结果,这是一个对源Objectable发出的数据执行转换的运算符。换句话说,我希望这个逻辑,它使用map
并且不需要buildProp
来返回一个Objectable,可以工作。
class MyService {
query() {
return from(myApiCall()).pipe(
pluck('data'),
map(apiResponse => {
const obj = apiResponse.foo.map(el => {
return {
bar: el.bar,
otherProp: el.baz,
builtProp: this.buildProp(el.myProp)
}
})
return obj
)
}
buildProp(value) {
if(!value) {
return throwError('value missing')
}
return `the value is: ${value}`
}
}
第二点是关于何时使用mergeMap
和其他类似的运算符,如SwitchMap
、exaustMap
、concatMap
,它们都是接受返回另一个可观察对象的输入函数的运算符。
此类运算符用于“展平”内部可观察对象,并最终创建一个可观察对象,该可观察对象发出内部可观察对象发出的数据。使用示例解释这个概念可能更容易。考虑您必须调用第一个RestAPI(超文本传输协议上的异步调用),并使用返回的数据作为输入来调用第二个RestAPI。要管理这种情况,您应该编写如下代码
firstApi().pipe(
concatMap(firstResult => secondApi(firstResult)
).subscribe(
// secondResult is the result returned by the invocation of secondApi
secondResult => {// do stuff with secondResult}
)
在这种情况下,如果您使用map
而不是concatMap
(这在某种程度上类似于您的合并映射),您将看到发出的是一个可观察的结果,而不是您想要的第二个Api
的结果。
如果您想阅读有关超文本传输协议场景中使用RxJs的典型模式的更多详细信息,可以查看本文。