Javascript 使用angular、HttpClient和Observable避免回调地狱
我目前正努力将我的头绕在angular(2+)、HttpClient和Observable上 我来自promise async/await的背景,我希望在angular中实现的目标相当于: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
//(...) 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将完成第一个可观察到的全部内容,然后再继续第二个。这也可以消除比赛条件,但不会取消旧的工作
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