Practical observable usage


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


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.


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

    防抖(这样才能防止连续按键时每次按键都发起 API 请求,而应该等到按键出现停顿时才发起)

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


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

    如果已发出的 AJAX 请求的结果会因为后续的修改而变得无效,那就取消它。

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

完全用 JavaScript 的传统写法实现这个功能可能需要大量的工作。使用可观察对象,你可以使用这样一个 RxJS 操作符的简单序列:

import { fromEvent } from 'rxjs'; import { ajax } from 'rxjs/ajax'; import { map, filter, debounceTime, distinctUntilChanged, switchMap } from 'rxjs/operators'; const searchBox = document.getElementById('search-box'); const typeahead = fromEvent(searchBox, 'input').pipe( map((e: KeyboardEvent) =>, filter(text => text.length > 2), debounceTime(10), distinctUntilChanged(), switchMap(() => ajax('/api/endpoint')) ); typeahead.subscribe(data => { // Handle the data from the API });

Exponential backoff


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:

指数化退避是一种失败后重试 API 的技巧,它会在每次连续的失败之后让重试时间逐渐变长,超过最大重试次数之后就会彻底放弃。 如果使用承诺和其它跟踪 AJAX 调用的方法会非常复杂,而使用可观察对象,这非常简单:

import { pipe, range, timer, zip } from 'rxjs'; import { ajax } from 'rxjs/ajax'; import { retryWhen, map, mergeMap } from 'rxjs/operators'; function backoff(maxTries, ms) { return pipe( retryWhen(attempts => range(1, maxTries) .pipe( zip(attempts, (i) => i), map(i => i * i), mergeMap(i => timer(i * ms)) ) ) ); } ajax('/api/endpoint') .pipe(backoff(3, 250)) .subscribe(data => handleData(data)); function handleData(data) { // ... }