提问者:小点点

Angular服务的方法应该总是只返回可观察对象吗?


我是可观察对象的新手,我很难理解如何正确使用它们。

假设我有这项服务:

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的可观察对象?


共1个答案

匿名用户

我喜欢在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和其他类似的运算符,如SwitchMapexaustMapconcatMap,它们都是接受返回另一个可观察对象的输入函数的运算符。

此类运算符用于“展平”内部可观察对象,并最终创建一个可观察对象,该可观察对象发出内部可观察对象发出的数据。使用示例解释这个概念可能更容易。考虑您必须调用第一个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的典型模式的更多详细信息,可以查看本文。