使用 RxJs Observable 来避免 Angular 应用中的 Promise 使用
我們通過一個具體的例子來闡述。
考慮您正在構建一個搜索輸入掩碼,該掩碼應在您鍵入時立即顯示結果。
如果您曾經構建過這樣的東西,那么您可能會意識到該任務帶來的挑戰。
將搜索端點視為您按請求付費。不管它是不是你自己的硬件。我們不應該比需要的更頻繁地敲擊搜索端點。基本上我們只想在用戶停止輸入后點擊它,而不是每次擊鍵時點擊它。
假設您鍵入 foo,停止,鍵入另一個 o,然后立即退格并返回到 foo。這應該只是一個帶有 foo 一詞的請求,而不是兩個,即使我們在搜索框中有 foo 后從技術上講停止了兩次。
3.處理亂序響應
當我們同時有多個請求進行中時,我們必須考慮它們以意外順序返回的情況??紤]我們首先鍵入 computer,停止,請求發出,我們鍵入 car,停止,請求發出?,F在我們有兩個正在進行的請求。不幸的是,在為 car 攜帶結果的請求之后,為 computer 攜帶結果的請求又回來了。這可能是因為它們由不同的服務器提供服務。如果我們不正確處理此類情況,我們最終可能會顯示 computer 的結果,而搜索框會顯示 car.
我們將使用免費和開放的維基百科 API 來編寫一個小演示。
為簡單起見,我們的演示將只包含兩個文件:app.ts 和 wikipedia-service.ts。 不過,在現實世界中,我們很可能會將事情進一步拆分。
讓我們從一個基于 Promise 的實現開始,它不處理任何描述的邊緣情況。
這就是我們的 WikipediaService 的樣子。
使用了 jsonp 這個 Angular HTTP 服務:
上圖將來自 angular/http 庫中的 jsonp 返回的對象,使用 toPromise 方法轉換成了 promise.
簡單地說,我們正在注入 Jsonp 服務,以使用給定的搜索詞針對維基百科 API 發出 GET 請求。 請注意,我們調用 toPromise 是為了從 Observable<Response> 到 Promise<Response>。 通過 then-chaining 我們最終得到一個 Promise<Array<string>> 作為我們搜索方法的返回類型。
到目前為止一切順利,讓我們看看保存我們的 App 組件的 app.ts 文件。
看一下 wiki service 如何被消費的:
這里也沒什么驚喜。 我們注入我們的 WikipediaService 并通過搜索方法向模板公開它的功能。 模板簡單地綁定到 keyup 并調用 search(term.value) 利用 Angular 的很棒的模板引用功能。
我們解開 WikipediaService 的搜索方法返回的 Promise 的結果,并將其作為一個簡單的字符串數組公開給模板,這樣我們就可以讓 *ngFor 循環遍歷它并為我們構建一個列表。
不幸的是,這個實現沒有解決我們想要處理的任何所描述的邊緣情況。 讓我們重構我們的代碼,使其符合預期的行為。
讓我們更改我們的代碼,不要在每次擊鍵時敲擊端點,而是僅在用戶停止輸入 400 毫秒時發送請求。 這就是 Observables 真正閃耀的地方。 Reactive Extensions (Rx) 提供了廣泛的運算符,讓我們可以改變 Observables 的行為并創建具有所需語義的新 Observables。
為了揭示這樣的超能力,我們首先需要獲得一個 Observable<string> ,它攜帶用戶輸入的搜索詞。 我們可以利用 Angular 的 formControl 指令,而不是手動綁定到 keyup 事件。 要使用此指令,我們首先需要將 ReactiveFormsModule 導入到我們的應用程序模塊中。
導入后,我們可以在模板中使用 formControl 并將其設置為名稱“term”。
<input type="text" [formControl]="term"/>在我們的組件中,我們從@angular/form 創建了一個 FormControl 的實例,并將其公開為組件上名稱 term 下的一個字段。
在幕后,術語會自動公開一個 Observable<string> 作為我們可以訂閱的屬性 valueChanges。 現在我們有了一個 Observable<string>,馴服用戶輸入就像在我們的 Observable 上調用 debounceTime(400) 一樣簡單。 這將返回一個新的 Observable<string>,它只會在 400 毫秒內沒有新值出現時才發出新值。
這個新的對象會在我們期望的時間間隔之后,才會發生新值。至于如何控制時間間隔,對前端開發人員來說是一個黑盒子。
export class App {items: Array<string>;term = new FormControl();constructor(private wikipediaService: WikipediaService) {this.term.valueChanges.debounceTime(400).subscribe(term => this.wikipediaService.search(term).then(items => this.items = items));} }正如我們所說,對我們的應用程序已經顯示結果的搜索詞發出另一個請求將是一種資源浪費。幸運的是,Rx 簡化了許多幾乎不需要提及的操作。為了實現所需的行為,我們所要做的就是在我們調用 debounceTime(400) 之后立即調用 distinctUntilChanged 運算符。同樣,我們將返回一個 Observable<string> ,但它忽略了與前一個相同的值。
處理無序響應
處理無序響應可能是一項棘手的任務。基本上,我們需要一種方法來表示,一旦我們發出新請求,我們就不再對之前進行中的請求的結果感興趣。換句話說:一旦我們開始一個新的請求,就取消所有先前的請求。正如我在開頭簡要提到的,Observables 是一次性的,這意味著我們可以取消訂閱它們。
這是我們想要更改 WikipediaService 以返回 Observable<Array<string>> 而不是 Promise<Array<string>> 的地方。這就像刪除 toPromise 并使用 map 而不是 then 一樣簡單。
search (term: string) {var search = new URLSearchParams()search.set('action', 'opensearch');search.set('search', term);search.set('format', 'json');return this.jsonp.get('http://en.wikipedia.org/w/api.php?callback=JSONP_CALLBACK', { search }).map((response) => response.json()[1]); }現在我們的 WikipediaSerice 返回一個 Observable 而不是 Promise,我們只需要在我們的 App 組件中將 then 替換為 subscribe 。
this.term.valueChanges.debounceTime(400).distinctUntilChanged().subscribe(term => this.wikipediaService.search(term).subscribe(items => this.items = items));但是現在我們有兩個 subscribe 調用。 這是不必要的冗長,通常是需要代碼重構的標志。 好消息是,現在搜索返回一個 Observable<Array>,我們可以簡單地使用 flatMap 通過組合 Observables 來將 Observable 投影到所需的 Observable<Array> 中。
this.term.valueChanges.debounceTime(400).distinctUntilChanged().flatMap(term => this.wikipediaService.search(term)).subscribe(items => this.items = items);你可能想知道 flatMap 是做什么的,為什么我們不能在這里使用 map。
答案很簡單。 map 操作符需要一個函數,它接受一個值 T 并返回一個值 U。例如一個接受一個字符串并返回一個數字的函數。 因此,當您使用 map 時,您會從 Observable<T> 獲得一個 Observable<U>。 但是,我們的搜索方法本身會生成一個 Observable。 因此,來自我們在 distinctUntilChanged 之后的 Observable,map 會將我們帶到 Observable<Observable<Array>。 這不是我們想要的。
另一方面, flatMap 運算符需要一個函數,它接受一個 T 并返回一個 Observable<U> 并為我們生成一個 Observable<U>。
注意:這并不完全正確,但它有助于簡化。
這完全符合我們的情況。 我們有一個 Observable<string>,然后使用一個函數調用 flatMap,該函數接受一個字符串并返回一個 Observable<Array<string>>。
現在我們已經掌握了語義,還有一個小技巧可以用來節省一些打字的時間。 我們可以讓 Angular 直接在模板中為我們解包,而不是手動訂閱 Observable。 我們要做的就是在我們的模板中使用 AsyncPipe 并公開 Observable<Array<string>> 而不是 Array<string>。
@Component({selector: 'my-app',template: `<div><h2>Wikipedia Search</h2><input type="text" [formControl]="term"/><ul><li *ngFor="let item of items | async">{{item}}</li></ul></div>` }) export class App {items: Observable<Array<string>>;term = new FormControl();constructor(private wikipediaService: WikipediaService) {this.items = this.term.valueChanges.debounceTime(400).distinctUntilChanged().switchMap(term => this.wikipediaService.search(term));} }更多Jerry的原創文章,盡在:“汪子熙”:
總結
以上是生活随笔為你收集整理的使用 RxJs Observable 来避免 Angular 应用中的 Promise 使用的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 抖音火山版私信消息怎么关闭
- 下一篇: 巴菲特:人工智能可以改变一切 但不会超过