async 概述
Angular 框架中集成了 Rxjs ,各类业务场景可以使用 Observable 进行处理。 关于 Observable , 可以参考 Angular 记录 - Observable 概述 。
由于 Observable 是惰性的,他需要被主动订阅去触发函数,在订阅之后还需要考虑 Observer 的回收问题,因此 Angular 为 Observable 这种异常操作专门设计了一个管道操作符 Async。
async 使用示例
通过下面这个例子,可以对比使用 Async 管道所带来的诸多益处:
import { Component, OnInit, OnDestroy } from '@angular/core';
import { Observable, Subscription, fromEvent } from 'rxjs';
import { takeUntil, interval, map, map } from 'rxjs/operators';
@Component({
selector: 'app-test',
template: `CurrentTime: {{ time | date }}`
})
export class AppTestComponent implements OnInit, OnDestroy {
time: Date;
timeSubscription: Subscription;
ngOnInit() {
this.timeSubscription = Observable
.interval(1000)
.map(val => new Date())
.subscribe(val => this.time = val);
}
ngOnDestroy() {
this.timeSubscription.unsubscribe();
}
}
在上面的代码片段中, 我们通过 interval 操作符,每 1000 ms获取一个 value 值,并使用 map 操作符将其映射为当前的时间戳。通过改变 time 变量来实现页面记时的功能。
在组件销毁阶段,我们对这个 Observer 进行回收处理。
这是相当多的样板代码,如果我们忘记取消订阅,就会有产生内存泄漏的风险
我们可以大大简化,这里 async 管道来实现相同的功能:
import { Component, OnInit, OnDestroy } from '@angular/core';
import { Observable, Subscription, fromEvent } from 'rxjs';
import { takeUntil, interval, map, map } from 'rxjs/operators';
@Component({
selector: 'app-test',
template: `Time: {{ time$ | async | date }}`
})
export class AppTestComponent {
time$ = Observable
.interval(1000)
.map(val => new Date());
}
Async pipe 负责订阅和变化检测,以及在组件被销毁时取消订阅。通过一个操作符,帮助我们省去了大量的代码去处理 Observable 的繁琐问题。
在实际开发中,类似的问题都应该选择 async 管道去解决,保持代码简洁,但是对于在组合流,高阶流使用场景下,async 管道就无能为力了。
加上 $ 符,是 angular 开发的一个默认规则,所有包含 § 符的对象或者变量,我们会默认这个对象是一个 Observable 对象。
async 官方源码
Async 管道操作符会帮助我们主动订阅去订阅一个 Observable 对象,获取最新值,并触发变化检测。当组件销毁时候,AsyncPipe 还会帮助我们取消订阅,避免内存泄漏,在结合实际使用中,我们已经可以猜到它的实现原理了:
以下源码来自 Angular 仓库
@Pipe({name: 'async', pure: false})
export class AsyncPipe implements OnDestroy, PipeTransform {
private _latestValue: any = null;
private _latestReturnedValue: any = null;
...
// 注入 ChangeDetectorRef 实例
constructor(private _ref: ChangeDetectorRef) {}
ngOnDestroy(): void {
// 组件销毁时,调用 _dispose 方法
if (this._subscription) {
this._dispose();
}
}
transform(obj: Observable<any>|Promise<any>|null|undefined): any {
if (!this._obj) {
if (obj) {
// 对传入的 Observable 执行订阅操作
this._subscribe(obj);
}
this._latestReturnedValue = this._latestValue;
return this._latestValue;
}
...
}
private _subscribe(obj: Observable<any>|Promise<any>|EventEmitter<any>): void {
this._obj = obj;
this._strategy = this._selectStrategy(obj);
// 保存 subscription 对象,并执行 markForCheck 检测
this._subscription = this._strategy.createSubscription(
obj, (value: Object) => this._updateLatestValue(obj, value));
}
...
private _dispose(): void {
this._strategy.dispose(this._subscription !);
this._latestValue = null;
this._latestReturnedValue = null;
this._subscription = null;
this._obj = null;
}
private _updateLatestValue(async: any, value: Object): void {
if (async === this._obj) {
this._latestValue = value;
this._ref.markForCheck();
}
}
}
Async 管道符处理了在使用 Observable 时诸多麻烦事,因此在实际的业务场景中,我们应多使用 async 管道符来管理我们的数据。
async 更多使用示例
由于 Angular 很多业务流都可以用 Observable 解决。Async 操作符在项目中自然会大量的使用了,我们还可以结合很多场景来使用:
- 在父子组件传参中使用:
<home-risk-card [CardModel]="item | async"></home-risk-card>
- 结合 *ngFor, *ngIf 等指令使用:
<nz-col *ngFor="let item of riskData$ | async" [nzSpan]="8">
<home-risk-card [CardModel]="item"></home-risk-card>
</nz-col>
<div class="app-test" *ngIf="(riskData$ | async)?.length > 0"></div>
- 结合变量来使用:
<ng-container *ngIf="info$ | async as info">
<h1>{{info.titile}}</h1>
<div>
{{info.content}}
</div>
</ng-container>
- 结合 @ngrx/store 使用:
...
@Component({
selector: 'app-test2',
template: `
<risk-info-list [books]="infoList$ | async"></risk-info-list>
`,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class TestComponent {
infoList$: Observable<Info[]>;
constructor(store: Store<AppState>) {
this.infoList$ = store.select(getInfoListSelector);
}
...
}
希望可以帮到你.
感谢您的阅读~