填寫這份《一分鐘調查》,幫我們(開發組)做得更好!去填寫Home

可觀察物件用法實戰

Practical observable usage

這裡示範了一些在某種領域中可觀察物件會特別有用的例子。

Here are some examples of domains in which observables are particularly useful.

輸入提示(type-ahead)建議

Type-ahead suggestions

可觀察物件可以簡化輸入提示建議的實現方式。典型的輸入提示要完成一系列獨立的任務:

Observables can simplify the implementation of type-ahead suggestions. Typically, a type-ahead has to do a series of separate tasks:

  • 從輸入中監聽資料。

    Listen for data from an input.

  • 移除輸入值前後的空白字元,並確認它達到了最小長度。

    Trim the value (remove whitespace) and make sure it’s a minimum length.

  • 防抖(這樣才能防止連續按鍵時每次按鍵都發起 API 請求,而應該等到按鍵出現停頓時才發起)

    Debounce (so as not to send off API requests for every keystroke, but instead wait for a break in keystrokes).

  • 如果輸入值沒有變化,則不要發起請求(比如按某個字元,然後快速按退格)。

    Don’t send a request if the value stays the same (rapidly hit a character, then backspace, for instance).

  • 如果已發出的 AJAX 請求的結果會因為後續的修改而變得無效,那就取消它。

    Cancel ongoing AJAX requests if their results will be invalidated by the updated results.

完全用 JavaScript 的傳統寫法實現這個功能可能需要大量的工作。使用可觀察物件,你可以使用這樣一個 RxJS 運算子的簡單序列:

Writing this in full JavaScript can be quite involved. With observables, you can use a simple series of RxJS operators:

Typeahead
      
      import { fromEvent, Observable } from 'rxjs';
import { debounceTime, distinctUntilChanged, filter, map, switchMap } from 'rxjs/operators';

const searchBox = document.getElementById('search-box') as HTMLInputElement;

const typeahead = fromEvent(searchBox, 'input').pipe(
  map(e => (e.target as HTMLInputElement).value),
  filter(text => text.length > 2),
  debounceTime(10),
  distinctUntilChanged(),
  switchMap(searchTerm => ajax(`/api/endpoint?search=${searchTerm}`))
);

typeahead.subscribe(data => {
  // Handle the data from the API
});
    

指數化退避

Exponential backoff

指數化退避是一種失敗後重試 API 的技巧,它會在每次連續的失敗之後讓重試時間逐漸變長,超過最大重試次數之後就會徹底放棄。 如果使用承諾和其它追蹤 AJAX 呼叫的方法會非常複雜,而使用可觀察物件,這非常簡單:

Exponential backoff is a technique in which you retry an API after failure, making the time in between retries longer after each consecutive failure, with a maximum number of retries after which the request is considered to have failed. This can be quite complex to implement with promises and other methods of tracking AJAX calls. With observables, it is very easy:

Exponential backoff
      
      import { of, pipe, range, throwError, timer, zip } from 'rxjs';
import { ajax } from 'rxjs/ajax';
import { map, mergeMap, retryWhen } from 'rxjs/operators';

export function backoff(maxTries: number, delay: number) {
  return pipe(
    retryWhen(attempts =>
      zip(range(1, maxTries + 1), attempts).pipe(
        mergeMap(([i, err]) => (i > maxTries) ? throwError(err) : of(i)),
        map(i => i * i),
        mergeMap(v => timer(v * delay)),
      ),
    ),
  );
}

ajax('/api/endpoint')
  .pipe(backoff(3, 250))
  .subscribe(function handleData(data) { /* ... */ });