Javascript 使用angular、HttpClient和Observable避免回调地狱

Javascript 使用angular、HttpClient和Observable避免回调地狱,javascript,angular,typescript,promise,observable,Javascript,Angular,Typescript,Promise,Observable,我目前正努力将我的头绕在angular(2+)、HttpClient和Observable上 我来自promise async/await的背景,我希望在angular中实现的目标相当于: //(...) Some boilerplate to showcase how to avoid callback hell with promises and async/await async function getDataFromRemoteServer() { this.result

我目前正努力将我的头绕在angular(2+)、HttpClient和Observable上

我来自promise async/await的背景,我希望在angular中实现的目标相当于:

//(...) Some boilerplate to showcase how to avoid callback hell with promises and async/await
  async function getDataFromRemoteServer() {
    this.result = await httpGet(`/api/point/id`);
    this.dependentKey = someComplexSyncTransformation(this.result);
    this.dependentResult = await httpGet(`/api/point/id/dependent/keys/${this.dependentKey}`);
    this.deeplyNestedResult = await httpGet(`/api/point/id/dependen/keys/${this.dependentResult.someValue}`);
  }
在angular中,我能提供的最佳方案是:

import { HttpClient } from `@angular/common/http`;

//(...) boilerplate to set component up.

  constructor(private http: HttpClient) {}

// somewhere in a component.

  getDataFromRemoteServer() {
    this.http.get(`/api/point/id`).subscribe( result => {
       this.result = result;
       this.dependentKey = someComplexSyncTransformation(this.result);
       this.http.get(`/api/point/id/dependent/keys/${this.dependentKey}`).subscribe( dependentResult => {
         this.dependentResult = dependentResult;
         this.http.get(`/api/point/id/dependen/keys/${this.dependentResult.someValue}`).subscribe( deeplyNestedResult => {
            this.deeplyNestedResult = deeplyNestedResult;
         });
       })
    });
  }

//...
正如你可能已经注意到的,我正以这种方式进入末日金字塔,我希望避免这种情况。 那么,我如何编写角度片段来避免这种情况呢

谢谢

Ps:我知道你可以打电话给我。就电话的结果向我保证。
但是现在我们假设我想采用完全可观察的方式。

在处理可观察对象时,您不会经常调用subscribe。相反,您将使用各种操作符将可观测值组合在一起,形成一条操作管道

要获取一个可观察对象的输出并将其转换为另一个,基本操作符是
map
。这与将一个数组映射为另一个数组的方式类似。举一个简单的例子,这里是一个可观测值的所有值加倍:

const myObservable = of(1, 2, 3).pipe(
  map(val => val * 2)
);
// myObservable is an observable which will emit 2, 4, 6
映射也是为一个http请求获取一个可观察对象,然后发出另一个http请求。但是,我们还需要一个附加部分,因此以下代码并不完全正确:

const myObservable = http.get('someUrl').pipe(
  map(result => http.get('someOtherUrl?id=' + result.id)
)
这段代码的问题在于,它创建了一个可观察的对象,而该对象会吐出其他可观察的对象。一个二维的可观察的,如果你喜欢。我们需要将其展平,这样我们就有了一个可观察的对象,可以吐出第二个http.get的结果。有几种不同的方法来进行展平,这取决于如果多个观测值发射多个值,我们希望结果的顺序。在您的例子中,这不是什么大问题,因为这些http观察对象中的每一个只会发出一项。但以下是供参考的选项:

  • mergeMap将让所有可观察对象以任何顺序运行,并以值到达的任何顺序输出。这有它的用途,但也可能导致比赛条件
  • switchMap将切换到最新的可观察对象,并取消可能正在进行的旧对象。这可以消除竞争条件,并确保您只有最新的数据
  • concatMap将完成第一个可观察到的全部内容,然后再继续第二个。这也可以消除比赛条件,但不会取消旧的工作
正如我所说,这对您的情况没有多大影响,但我建议您使用switchMap。所以我上面的小例子会变成:

const myObservable = http.get('someUrl').pipe(
  switchMap(result => http.get('someOtherUrl?id=' + result.id)
)

下面是我如何在代码中使用这些工具。在这个代码示例中,我没有保存所有this.result、this.dependentKey等:

  getDataFromRemoteServer() {
    return this.http.get(`/api/point/id`).pipe(
      map(result => someComplexSyncTransformation(result)),
      switchMap(dependentKey => this.http.get(`/api/point/id/dependent/keys/${dependentKey}`)),
      switchMap(dependantResult => this.http.get(`/api/point/id/dependent/keys/${dependentResult.someValue}`)
    });
  }

// to be used like:

   getDataFromRemoteServer()
     .subscribe(deeplyNestedResult => {
       // do whatever with deeplyNestedResult
     });
如果保存这些值对您很重要,那么我建议使用tap操作符突出显示您正在产生副作用的事实。tap将在observable发出值时运行一些代码,但不会干扰值:

  getDataFromRemoteServer() {
    return this.http.get(`/api/point/id`).pipe(
      tap(result => this.result = result),
      map(result => someComplexSyncTransformation(result)),
      tap(dependentKey => this.dependentKey = dependentKey),
      // ... etc
    });
  }

这个.result
这个.dependentKey
有什么关系?在类中的其他任何地方,除了这个计算之外,您还需要它们吗?您可以使用RxJS中的运算符来实现这一点。我无法确定您在“末日金字塔”中执行的操作类型,因此我无法为您指定任何RxJS运算符,但有些运算符可以进行映射、筛选等操作,您可以在初始
结果中使用它们。其次,您应该为该操作使用服务。在我看来,您正在从组件访问HttpClient