cec*_*mel 2 javascript promise observable typescript angular
我目前正在努力理解 Angular (2+)、HttpClient 和 Observables。
我来自承诺异步/等待背景,我想在角度中实现的目标相当于:
//(...) 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}`);
}
Run Code Online (Sandbox Code Playgroud)
我能想到的最好的角度是:
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;
});
})
});
}
//...
Run Code Online (Sandbox Code Playgroud)
正如您可能已经注意到的,我正在通过这种方法进入末日金字塔,我想避免这种方法。那么我该如何编写角度片段以避免这种情况呢?
谢谢!
Ps:我知道您可以根据 .get 调用的结果调用 .toPromise。但现在我们假设我想采用完全可观察的方式。
使用可观察量时,您不会经常调用 subscribe。相反,您将使用各种运算符将可观察量组合在一起,形成操作管道。
要获取一个可观察量的输出并将其转换为另一个可观察量的输出,基本运算符是map。这类似于如何用.map一个数组生成另一个数组。举一个简单的例子,这里将可观察值的所有值加倍:
const myObservable = of(1, 2, 3).pipe(
map(val => val * 2)
);
// myObservable is an observable which will emit 2, 4, 6
Run Code Online (Sandbox Code Playgroud)
映射也是您为一个 http 请求获取可观察值,然后发出另一个 http 请求的操作。然而,我们需要额外的一块,所以下面的代码不太正确:
const myObservable = http.get('someUrl').pipe(
map(result => http.get('someOtherUrl?id=' + result.id)
)
Run Code Online (Sandbox Code Playgroud)
这段代码的问题在于它创建了一个可观察量,该可观察量会吐出其他可观察量。如果你愿意的话,可以是二维可观察的。我们需要将其扁平化,以便我们有一个可观察的对象来输出第二个 http.get 的结果。有几种不同的方法可以进行扁平化,具体取决于我们希望多个可观察量发出多个值时结果的顺序。对于您的情况来说,这并不是什么大问题,因为每个 http observables 只会发出一项。但作为参考,以下是选项:
就像我说的,这对你的情况来说并不重要,但我建议使用 switchMap。所以我上面的小例子将变成:
const myObservable = http.get('someUrl').pipe(
switchMap(result => http.get('someOtherUrl?id=' + result.id)
)
Run Code Online (Sandbox Code Playgroud)
现在,我将介绍如何在您的代码中使用这些工具。在此代码示例中,我没有保存所有 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
});
Run Code Online (Sandbox Code Playgroud)
如果保存这些值对您很重要,那么我建议使用 tap 运算符来突出显示您正在产生副作用的事实。每当 observable 发出一个值时,tap 就会运行一些代码,但不会弄乱该值:
getDataFromRemoteServer() {
return this.http.get(`/api/point/id`).pipe(
tap(result => this.result = result),
map(result => someComplexSyncTransformation(result)),
tap(dependentKey => this.dependentKey = dependentKey),
// ... etc
});
}
Run Code Online (Sandbox Code Playgroud)