{ "id": "guide/comparing-observables", "title": "Observables compared to other techniques", "contents": "\n\n\n
\n mode_edit\n
\n\n\n
\n

Observables compared to other techniqueslink

\n

You can often use observables instead of promises to deliver values asynchronously. Similarly, observables can take the place of event handlers. Finally, because observables deliver multiple values, you can use them where you might otherwise build and operate on arrays.

\n

Observables behave somewhat differently from the alternative techniques in each of these situations, but offer some significant advantages. Here are detailed comparisons of the differences.

\n

Observables compared to promiseslink

\n

Observables are often compared to promises. Here are some key differences:

\n\n

Creation and subscriptionlink

\n\n

Chaininglink

\n\n

Cancellationlink

\n\n

Error handlinglink

\n\n

Cheat sheetlink

\n

The following code snippets illustrate how the same kind of operation is defined using observables and promises.

\n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
OperationObservablePromise
Creation\n
new Observable((observer) => {\n  observer.next(123);\n});
\n
\n
new Promise((resolve, reject) => {\n  resolve(123);\n});
\n
Transform
obs.pipe(map((value) => value * 2));
promise.then((value) => value * 2);
Subscribe\n
sub = obs.subscribe((value) => {\n  console.log(value)\n});
\n
\n
promise.then((value) => {\n  console.log(value);\n});
\n
Unsubscribe
sub.unsubscribe();
Implied by promise resolution.
\n

Observables compared to events APIlink

\n

Observables are very similar to event handlers that use the events API. Both techniques define notification handlers, and use them to process multiple values delivered over time. Subscribing to an observable is equivalent to adding an event listener. One significant difference is that you can configure an observable to transform an event before passing the event to the handler.

\n

Using observables to handle events and asynchronous operations can have the advantage of greater consistency in contexts such as HTTP requests.

\n

Here are some code samples that illustrate how the same kind of operation is defined using observables and the events API.

\n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
ObservableEvents API
Creation & cancellation\n
// Setup\nconst clicks$ = fromEvent(buttonEl, ‘click’);\n// Begin listening\nconst subscription = clicks$\n  .subscribe(e => console.log(‘Clicked’, e))\n// Stop listening\nsubscription.unsubscribe();
\n
\n
function handler(e) {\n  console.log(‘Clicked’, e);\n}\n// Setup & begin listening\nbutton.addEventListener(‘click’, handler);\n// Stop listening\nbutton.removeEventListener(‘click’, handler);\n
\n
Subscription\n
observable.subscribe(() => {\n  // notification handlers here\n});
\n
\n
element.addEventListener(eventName, (event) => {\n  // notification handler here\n});
\n
ConfigurationListen for keystrokes, but provide a stream representing the value in the input.\n
fromEvent(inputEl, 'keydown').pipe(\n  map(e => e.target.value)\n);
\n
Does not support configuration.\n
element.addEventListener(eventName, (event) => {\n  // Cannot change the passed Event into another\n  // value before it gets to the handler\n});
\n
\n

Observables compared to arrayslink

\n

An observable produces values over time. An array is created as a static set of values. In a sense, observables are asynchronous where arrays are synchronous. In the following examples, ➞ implies asynchronous value delivery.

\n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
ObservableArray
Given\n
obs: ➞1➞2➞3➞5➞7
\n
obsB: ➞'a'➞'b'➞'c'
\n
\n
arr: [1, 2, 3, 5, 7]
\n
arrB: ['a', 'b', 'c']
\n
concat()
\n
concat(obs, obsB)
\n
➞1➞2➞3➞5➞7➞'a'➞'b'➞'c'
\n
\n
arr.concat(arrB)
\n
[1,2,3,5,7,'a','b','c']
\n
filter()
\n
obs.pipe(filter((v) => v>3))
\n
➞5➞7
\n
\n
arr.filter((v) => v>3)
\n
[5, 7]
\n
find()
\n
obs.pipe(find((v) => v>3))
\n
➞5
\n
\n
arr.find((v) => v>3)
\n
5
\n
findIndex()
\n
obs.pipe(findIndex((v) => v>3))
\n
➞3
\n
\n
arr.findIndex((v) => v>3)
\n
3
\n
forEach()
\n
obs.pipe(tap((v) => {\n  console.log(v);\n}))\n1\n2\n3\n5\n7
\n
\n
arr.forEach((v) => {\n  console.log(v);\n})\n1\n2\n3\n5\n7
\n
map()
\n
obs.pipe(map((v) => -v))
\n
➞-1➞-2➞-3➞-5➞-7
\n
\n
arr.map((v) => -v)
\n
[-1, -2, -3, -5, -7]
\n
reduce()
\n
obs.pipe(reduce((s,v)=> s+v, 0))
\n
➞18
\n
\n
arr.reduce((s,v) => s+v, 0)
\n
18
\n
\n\n \n
\n\n\n" }