更新時間 - 2017-03-21 15:02html
今天咱們來介紹一下 Angular 2 中 AsyncPipe (異步管道) ,使用 AsyncPipe 咱們能夠直接在模板中使用 Promise
和 Observable
對象,而不用經過定義一個類的成員屬性來存儲返回的結果。typescript
AsyncPipe 訂閱一個 Observable 或 Promise 對象,並返回它發出的最新值。 當發出新值時,異步管道會主動調用變化檢測器的 markForCheck()
方法,標識組件需執行變化檢測。 當組件被銷燬時,異步管道自動取消訂閱,以免潛在的內存泄漏。json
Promise 未使用 AsyncPipesegmentfault
promise.component.tspromise
import { Component } from '@angular/core'; @Component({ selector: 'exe-promise', template: ` <h4>Promise Component</h4> <p>{{promiseData}}</p> ` }) export class PromiseComponent { promiseData: string; constructor() { this.getPromise().then(v => this.promiseData = v); } getPromise(): Promise<string> { return new Promise((resolve, reject) => { setTimeout(() => { resolve('Promise complete!'); }, 2000); }); } }
app.component.ts瀏覽器
import { Component } from '@angular/core'; @Component({ selector: 'exe-app', template: ` <exe-promise></exe-promise> ` }) export class AppComponent { }
以上代碼運行後瀏覽器顯示的結果:安全
Promise 使用 AsyncPipe服務器
promise-async-pipe.component.ts網絡
import { Component } from '@angular/core'; @Component({ selector: 'exe-promise-pipe', template: ` <h4>Promise with AsyncPipeComponent</h4> <p>{{ promise | async }}</p> ` }) export class PromiseAsyncPipeComponent { promise: Promise<string>; constructor() { this.promise = this.getPromise(); } getPromise(): Promise<string> { return new Promise((resolve, reject) => { setTimeout(() => { resolve('Promise with AsyncPipe complete!'); }, 2000); }); } }
app.component.tsapp
import { Component } from '@angular/core'; @Component({ selector: 'exe-app', template: ` <exe-promise-pipe></exe-promise-pipe> ` }) export class AppComponent { }
以上代碼運行後瀏覽器顯示的結果:
Observable 未使用 AsyncPipe
observable.component.ts
import { Observable, Subscription } from 'rxjs/Rx'; import { Component, OnDestroy } from '@angular/core'; @Component({ selector: 'exe-observable', template: ` <h4>Observable Component</h4> <p>{{ observableData }}</p> ` }) export class ObservableComponent implements OnDestroy { observableData: number; subscription: Subscription = null; constructor() { this.subscribeObservable(); } getObservable(): Observable<number> { return Observable .interval(1000) // 每隔1秒,生成一個值 .take(10) // 獲取前面10個數據 .map(v => v * v); // 對每一個數據進行乘方處理 } subscribeObservable() { this.subscription = this.getObservable() .subscribe(v => this.observableData = v); } ngOnDestroy() { // 組件銷燬時取消訂閱,以免潛在的內存泄漏 if (this.subscription) { this.subscription.unsubscribe(); } } }
app.component.ts
import { Component } from '@angular/core'; @Component({ selector: 'exe-app', template: ` <exe-observable></exe-observable> ` }) export class AppComponent { }
以上代碼運行後瀏覽器顯示的結果:
Observable 使用 AsyncPipe
observable-async-pipe.component.ts
import { Component } from '@angular/core'; import { Observable } from 'rxjs/Rx'; @Component({ selector: 'exe-observable-pipe', template: ` <h4>Observable with AsyncPipe Component</h4> <p>{{ observable | async }}</p> ` }) export class ObservableAsyncPipeComponent { observable: Observable<number> constructor() { this.observable = this.getObservable(); } getObservable(): Observable<number> { return Observable .interval(1000) .take(10) .map(v => v * v); } }
app.component.ts
import { Component } from '@angular/core'; @Component({ selector: 'exe-app', template: ` <exe-observable-pipe></exe-observable-pipe> ` }) export class AppComponent { }
以上代碼運行後瀏覽器顯示的結果:
爲了更讓讀者更好地掌握 AsyncPipe, 咱們再來一個示例:
import { Component, OnInit } from '@angular/core'; import { Observable } from 'rxjs/Rx'; interface Hero { id: number; name: string; } @Component({ selector: 'async-pipe-example', template: ` <h4>Async Pipe Example</h4> <div> <h5>Heroes: </h5> <ul> <li *ngFor="let hero of heroes$ | async"> {{ hero.name }} </li> </ul> <h5>Hero: </h5> <p> <span *ngIf="hero$">{{ (hero$ | async).id }}</span> <span>{{ (hero$ | async)?.name }}</span> </p> </div> ` }) export class AsyncPipeComponent implements OnInit { heroes$: Observable<Hero[]>; hero$: Observable<Hero>; getHeroes(): Observable<Hero[]> { return Observable.of([ { id: 1, name: 'Windstorm' }, { id: 13, name: 'Bombasto' }, { id: 15, name: 'Magneta' }, { id: 20, name: 'Tornado' } ]); } getHero(): Observable<Hero> { return Observable.of({ id: 31, name: 'Semlinker' }); } ngOnInit() { setTimeout(() => { this.heroes$ = this.getHeroes(); this.hero$ = this.getHero(); }, 2000); } }
以上代碼運行後瀏覽器顯示的結果:
上面例子中有兩個注意點:
1.使用 ngIf
控制 span
元素的顯示:
<span *ngIf="hero$">{{ (hero$ | async).id }}</span>
2.使用 ?.
安全導航操做符,控制 name
屬性的顯示:
<span>{{ (hero$ | async)?.name }}</span>
若去掉 ngIf
或 ?.
,應用程序將會拋出異常,建議讀者親身體驗一下。
1.Promise vs Observable
Promise
返回單個值
不可取消的
Observable
隨着時間的推移發出多個值
能夠取消的
支持 map、filter、reduce 等操做符
延遲執行,當訂閱的時候纔會開始執行
詳細內容能夠參考 - RxJS 核心概念之Observable
2.使用 AsyncPipe 會發送屢次請求
咱們直接看一下具體示例:
app.component.ts
import { Component, OnInit } from '@angular/core'; import { Http } from '@angular/http'; import { Observable } from 'rxjs/Rx'; @Component({ selector: 'exe-app', template: ` <div> <p>{{ (person$ | async)?.id }}</p> <p>{{ (person$ | async)?.title }}</p> <p>{{ (person$ | async)?.body }}</p> </div> ` }) export class AppComponent implements OnInit { person$: Observable<{ id: number; title: string; body: string }>; constructor(private http: Http) { } ngOnInit() { this.person$ = this.http.get('https://jsonplaceholder.typicode.com/posts/1') .map(res => res.json()) } }
以上代碼運行後能正常顯示結果,但若是你切換到開發者工具的網絡面板,你會發現發送了三個重複的請求。這是由於咱們的 Observable 是 cold 的,每處使用 async 管道的地方都會執行一次。針對上述問題,大部分人推薦的解決方案以下:
this.http.get('https://jsonplaceholder.typicode.com/posts/1') .map(res => res.json()).share()
咱們只需使用 RxJS 中的共享操做符,它在內部調用 publish().refCount()。是否是很開心,但先等等,還有一種狀況又會觸發 HTTP 請求,具體咱們再來看一下示例:
import { Component, OnInit } from '@angular/core'; import { Http } from '@angular/http'; import { Observable } from 'rxjs/Rx'; @Component({ selector: 'exe-app', template: ` <div> <p>{{ (person$ | async)?.id }}</p> <p>{{ (person$ | async)?.title }}</p> <p>{{ (person$ | async)?.body }}</p> <button (click)="showPersonInfo()">顯示用戶ID</button> <p *ngIf="isShow">{{ (person$ | async)?.id }}</p> </div> ` }) export class AppComponent implements OnInit { person$: Observable<{ id: number; title: string; body: string }>; isShow: boolean = false; constructor(private http: Http) { } ngOnInit() { this.person$ = this.http.get('https://jsonplaceholder.typicode.com/posts/1') .map(res => res.json()) .share(); } showPersonInfo() { this.isShow = true; } }
以上代碼運行後瀏覽器顯示的結果:
咱們發現當點擊 '顯示用戶ID' 按鈕時,又觸發了新的請求。看來咱們的救世主 - share()
不給力了,幸運的是咱們還有新的救世主 - publishReplay()
,具體代碼以下:
import { Component, OnInit } from '@angular/core'; import { Http } from '@angular/http'; import { Observable, ConnectableObservable } from 'rxjs/Rx'; @Component({ selector: 'exe-app', template: ` <div> <p>{{ (person$ | async)?.id }}</p> <p>{{ (person$ | async)?.title }}</p> <p>{{ (person$ | async)?.body }}</p> <button (click)="showPersonInfo()">顯示用戶ID</button> <p *ngIf="isShow">{{ (person$ | async)?.id }}</p> </div> ` }) export class AppComponent implements OnInit { person$: ConnectableObservable<{ id: number; title: string; body: string }>; isShow: boolean = false; constructor(private http: Http) { this.preparePersonInfo(); } ngOnInit() { this.person$.connect(); } preparePersonInfo() { this.person$ = this.http.get('https://jsonplaceholder.typicode.com/posts/1') .map(res => res.json()) .publishReplay() } showPersonInfo() { this.isShow = true; } }
咱們使用 publishReplay 替換了 share 操做符。調用 publishReplay() 方法後將返回一個ConnectableObservable 對象,當你調用 connect() 方法的時候,將主動執行訂閱操做。
是否是感受快奔潰了,就想簡單的獲取用戶的信息,而後使用 AsyncPipe,怎麼就那麼多坑。。。
在大多數狀況下,咱們只須要從服務器獲取數據並顯示數據。若是隻是這樣的話,咱們能夠使用 Promise 來修復 AsyncPipe 發送屢次 HTTP 請求的問題:
this.person = this.http.get("https://jsonplaceholder.typicode.com/posts/1") .map(res => res.json()).toPromise()
3.AsyncPipe 執行流程
接下來咱們看一下 PromiseStrategy 與 ObservableStrategy 策略:
SubscriptionStrategy 接口
interface SubscriptionStrategy { createSubscription(async: any, updateLatestValue: any): any; dispose(subscription: any): void; onDestroy(subscription: any): void; }
PromiseStrategy
class PromiseStrategy implements SubscriptionStrategy { createSubscription(async: Promise<any>, updateLatestValue: (v: any) => any): any { return async.then(updateLatestValue, e => { throw e; }); } dispose(subscription: any): void {} onDestroy(subscription: any): void {} }
ObservableStrategy
class ObservableStrategy implements SubscriptionStrategy { createSubscription(async: any, updateLatestValue: any): any { return async.subscribe({next: updateLatestValue, error: (e: any) => { throw e; }}); } dispose(subscription: any): void { subscription.unsubscribe(); } // 取消訂閱 onDestroy(subscription: any): void { subscription.unsubscribe(); } }
這篇文章咱們介紹了 AsyncPipe 如何搭配 Promise 和 Observable 使用,此外介紹了 AsyncPipe 會發送屢次請求的問題,最後咱們得出的結論是若是隻是須要從服務器獲取數據並顯示數據,能夠使用 Promise 來修 AsyncPipe 發送屢次 HTTP 請求的問題。