Angular 服务的方法是否应该始终 return 可观察?

Should Angular services' methods always return observables only?

我是 observables 的新手,我很难理解如何正确使用它们。

说我有这个服务:

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 是一个 public 方法,我希望可以选择从 MyService class 之外的其他地方调用它。它的作用没有任何异步,所以它也可以直接 return 值而不是可观察值。但是,如果 MyService return observables 的某些方法和其他方法没有,那么使用起来可能会不一致且烦人。

我想到了一个私有方法 returns 构建对象时使用的纯字符串,还有另一个 public 方法 returns of(buildProp(val)),但这感觉很笨拙(不要介意现在为这两种不同的方法找到好名字的困难)。

Angular 服务上的任何 public 方法是否应该总是 return 可观察的,即使该方法的作用没有任何异步?

在那种特殊情况下,我如何以优雅且惯用的 Angular 方式在 returned 对象中获取文字字符串而不是 builtProp 的可观察值?

我喜欢在 Angular 中思考服务的方式是它们是放置任何类型的业务逻辑的地方,从复杂的异步内容到简单的数据同步转换。

为什么要将逻辑放在服务中?一个很好的理由是,这使代码更易于测试,并迫使您仅在组件中保留与可视化和管理用户交互严格相关的内容。

话虽如此,但没有理由认为服务必须 return Observables。他们必须 return 对 return 有意义的东西:异步逻辑情况下的 Observable,其他同步情况下合适的任何其他内容。

这里有一些关于您的具体案例的思考。

首先我质疑为什么要使用mergeMap?您应该可以使用 map 获得相同的结果,这是一个对源 Observable 发出的数据执行转换的运算符。换句话说,我希望这种使用 map 并且不需要 buildProp 到 return Observable 的逻辑能够工作。

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,它们都是接受作为输入函数的运算符 returning 另一个 Observables。

此类运算符用于“扁平化”内部 Observable 并最终创建一个 Observable,该 Observable 会发出内部 Observable 发出的数据。使用示例可能更容易解释这个概念。假设您必须调用第一个 Rest API(通过 http 的异步调用)并使用数据 returned 作为输入来调用第二个 Rest API。要处理这种情况,您应该编写类似这样的代码

firstApi().pipe(
  concatMap(firstResult => secondApi(firstResult)
).subscribe(
  // secondResult is the result returned by the invocation of secondApi
  secondResult => {// do stuff with secondResult}
)

在这种情况下,如果您使用 map 而不是 concatMap(这在某种程度上类似于您的 mergeMap),您会看到发出 Observable 而不是 [=21= 的结果],这不是你想要的。

如果您想阅读更多有关 RxJs 在 http 场景中的典型使用模式的详细信息,可以look at this article