Angular - 在基本的 HTTP POST 请求和承诺中,可观察对象对我有好处吗?
Posted
技术标签:
【中文标题】Angular - 在基本的 HTTP POST 请求和承诺中,可观察对象对我有好处吗?【英文标题】:Angular - would observables benefit me in a basic HTTP POST request vs promises? 【发布时间】:2020-09-27 07:35:27 【问题描述】:我正在使用 Angular 8 编写应用程序。我对来自 AngularJS 的框架有点陌生。我对 Promises 有很多经验。
我的后端 API 有 HTTP 请求。这些只是 GET 和 POST 调用,因为我们的 Delphi 后端没有任何 WebSocket。我已经成功调用了后端 API 并使用了 Promise。
我的代码调用后端 API 来获取我想要显示给用户的字符串值。
我想知道如何重构我的代码以使用 Observables,这样做是否有任何好处?
服务
import HttpClient from '@angular/common/http';
import Injectable from '@angular/core';
import ILicenseInfo from './ilicense-info';
@Injectable(
providedIn: 'root'
)
export class AboutService
constructor(private http: HttpClient)
public getVersionNumber()
const endpoint: string = 'myEndpoint';
const params =
password: 'yes'
;
return this.http.post<ILicenseInfo>(endpoint, params)
.toPromise()
.then((response: ILicenseInfo) =>
return response.versionNumberField;
);
组件
import Component, OnInit from '@angular/core';
import Constants from '../../../../app/core/constants/constants.service';
import AboutService from './about.service';
@Component(
selector: 'ns-about',
templateUrl: './about.component.html',
styleUrls: ['./about.component.css']
)
export class AboutComponent implements OnInit
public version: string;
constructor(private aboutService: AboutService)
public ngOnInit(): void
this.aboutService.getVersionNumber().then((response: string) =>
this.version = response;
);
模板
<FlexboxLayout>
<FlexboxLayout>
<Label text="Version:"></Label>
<Label text="version"></Label>
</FlexboxLayout>
</FlexboxLayout>
【问题讨论】:
【参考方案1】:RxJS 提供了许多operators 和方法来修改和控制来自可观察源的数据流。更详细的列表here。
例如。
合并两个请求
forkJoin([this.http.get('url'), this.http.post('url', '')]).subscribe
resposne =>
// resonse[0] - GET request response
// resonse[1] - POST request response
,
error =>
);
将一个调用的响应作为输入传递给另一个请求
this.http.get('url').pipe(
switchMap(getResponse =>
return this.http.post('url', getResponse)
).subscribe(
response => ,
error =>
);
这些是一些最普通的优势。当需要处理多个相互依赖的 observable 时,RxJS 会更有用。
在您的情况下,您可以通过管道输入 map
运算符以从响应中返回 versionNumberField
属性。
return this.http.post<ILicenseInfo>(endpoint, params).pipe(
map(response: ILicenseInfo => response.versionNumberField)
);
然后您可以订阅它的组件来检索发出的值
public ngOnInit(): void
this.aboutService.getVersionNumber().subscribe(
(response: string) =>
this.version = response;
);
潜在的内存泄漏
使用 observables,非关闭订阅存在潜在的内存泄漏风险。 Angular HTTP 客户端有一个内置的取消订阅机制,但这也可能会失败。所以最好在组件中关闭订阅(通常在ngOnDestroy()
钩子中)。
import Subscription from 'rxjs';
httpSubscription: Subscription;
public ngOnInit(): void
this.httpSubscription = this.aboutService.getVersionNumber().subscribe(
(response: string) =>
this.version = response;
);
ngOnDestroy()
if (this.httpSubscription)
this.httpSubscription.unsubscribe();
还有一种使用 RxJS takeUntil
运算符处理退订的优雅方式。
import Subject from 'rxjs';
import takeUntil from 'rxjs/operators';
closed = new Subject<void>();
public ngOnInit(): void
this.httpSubscription = this.aboutService.getVersionNumber().pipe(
takeUntil(this.closed)
).subscribe(
(response: string) =>
this.version = response;
);
ngOnDestroy()
this.closed.next();
this.closed.complete();
所以订阅将一直有效,直到 closed
可观察对象打开。
但是,如果您在多个组件中处理多个可观察对象,这两种方法都会很快变得乏味。 here 有一个巧妙的解决方法。
首先创建如下导出函数
// Based on https://www.npmjs.com/package/ng2-rx-componentdestroyed
// Source credit: https://***.com/a/45709120/6513921
import OnDestroy from '@angular/core';
import ReplaySubject, Observable from 'rxjs/ReplaySubject';
export function componentDestroyed(component: OnDestroy): Observable<void>
const oldNgOnDestroy = component.ngOnDestroy;
const destroyed$ = new ReplaySubject<void>(1);
component.ngOnDestroy = () =>
oldNgOnDestroy.apply(component);
destroyed$.next(undefined);
destroyed$.complete();
;
return destroyed$.asObservable();
现在要做的就是导入函数,在组件中实现ngOnDestroy
钩子并将takeUntil(componentDestroyed(this)
中的管道传递到源observable。
import takeUntil from 'rxjs/operators';
public ngOnInit(): void
this.httpSubscription = this.aboutService.getVersionNumber().pipe(
takeUntil(componentDestroyed(this)) // <-- pipe in the function here
).subscribe(
(response: string) =>
this.version = response;
);
ngOnDestroy() // <-- should be implemented
更新:async
管道
async
管道也可用于异步检索值。它适用于可观察对象和承诺。当与 observables 一起使用时,它确保在组件被销毁时订阅总是关闭,而控制器中没有任何修改。
控制器
version$: Observable<any>;
public ngOnInit(): void
this.version$ = this.aboutService.getVersionNumber();
模板
<ng-container *ngIf="(version$ | async) as version">
Version is version
<ng-container *ngIf="version > 5">
It is already the latest version available.
<ng-container>
</ng-container>
变量version$
以在Observable
类型后缀美元符号的通用约定命名。
【讨论】:
异步管道怎么样? @user1261710:忽略了它。我已经更新了答案。以上是关于Angular - 在基本的 HTTP POST 请求和承诺中,可观察对象对我有好处吗?的主要内容,如果未能解决你的问题,请参考以下文章
Angular 的 $http.post 在 MS Edge 中不起作用
Angular $http 正在发送 OPTIONS 而不是 PUT(不是 POST)
angular2 http.post 方法抛出 typeerror 异常