乐闻世界logo
搜索文章和话题

What is the difference between Promises and Observables?

5个答案

1
2
3
4
5

Promise and Observable are commonly used in asynchronous programming, especially in JavaScript and JavaScript-based frameworks (such as Angular). Although both handle asynchronous operations, their approaches and functionalities differ. Here are some key differences:

1. Single Value vs Multiple Values

  • Promise:
    • A Promise represents the eventual result of an asynchronous operation. It is designed for handling a single asynchronous operation and returns a single value.
  • Observable:
    • An Observable can emit multiple values, forming a stream of data. It can emit zero or more values and can continue indefinitely.

2. Eager vs Lazy

  • Promise:
    • Promises are eager, meaning they execute immediately once created.
  • Observable:
    • Observables are lazy. Execution (known as subscription) starts only when a subscriber is present.

3. Cancellation

  • Promise:
    • Once initiated, a Promise cannot be canceled. It either resolves with a value or rejects with an error.
  • Observable:
    • Observables can be canceled. Subscribers can unsubscribe, which stops the operation from executing.

4. Operators

  • Promise:
    • Promises offer limited built-in methods, including .then(), .catch(), and .finally().
  • Observable:
    • Observables support a broad set of operators, including map(), filter(), concat(), flatMap(), etc., enabling the processing of data within the stream.

5. Error Handling

  • Promise:
    • In Promises, errors are handled via rejection and can be caught using the .catch() method.
  • Observable:
    • In Observables, errors can be caught at any point in the stream and handled using dedicated error-handling operators.

6. Use Cases

  • Promise:
    • Promises are commonly used for single asynchronous tasks, particularly when dealing with one-time events.
  • Observable:
    • Observables are ideal for handling data streams, user input, HTTP requests, and more, particularly when dealing with multiple values or processing cancellations and continuous data streams.

In summary, Promises are better suited for simple asynchronous transformations, while Observables provide more powerful control for handling complex data streams and asynchronous events.

2024年6月29日 12:07 回复

Promise When an asynchronous operation completes or fails, a Promise handles a single event.

Note: Some Promise libraries support cancellation, but ES6 Promise does not support it as of now.

Observable An Observable is similar to a Stream (in many languages), allowing you to pass zero or more events, with a callback invoked for each event.

Observable is often preferred over Promise because it provides all the functionality of Promise and more. With Observable, you can handle 0, 1, or multiple events using the same API.

Observable also has the advantage of being cancellable. If you no longer need the results of an HTTP request to a server or other expensive asynchronous operations, a Subscription allows you to unsubscribe from the Observable, whereas a Promise will eventually call the success or failure callback even if you no longer need the notification or the results.

A Promise starts immediately, whereas an Observable only starts when you subscribe. This is why Observables are called lazy.

Observable provides operators similar to arrays, such as map, forEach, and reduce.

There are also powerful operators, such as retry(), replay(), etc., which are often very convenient. List of operators provided by RxJS

Lazy execution allows you to build the operator chain before subscribing to the Observable, enabling more declarative programming.

2024年6月29日 12:07 回复

The answer overlooks one drawback of Observables. Promises support ES2017 async/await functions, allowing you to write asynchronous code as if it were synchronous function calls, thereby eliminating the need for callbacks. The only way Observables can achieve this is by converting them to Promises. However, when you convert them to Promises, you can only obtain one return value:

javascript
async function getData() { const data = await observable.first().toPromise(); // do stuff with 'data' (no callback function needed) }

Further reading: How to 'await' on an Rx Observable?

2024年6月29日 12:07 回复

Promises and Observables both help us work with asynchronous functionality in JavaScript. They are very similar in many cases, but there are some key differences. Promises resolve values from asynchronous HTTP calls and similar operations. On the other hand, Observables handle a series of asynchronous events. The main differences are as follows:

Promises:

  • Have a single stream
  • Are typically used with asynchronous data responses
  • Are not easily cancellable

Observables:

  • Can be cancelled
  • Are inherently retryable, for example with retry and retryWhen
  • Transmit data across multiple streams
  • Have array-like operations, such as map and filter
  • Can be created from other sources like events
  • They are functions that can be subscribed to later

Additionally, I have created a visual image below to illustrate the differences:

Promises and Observables Image

2024年6月29日 12:07 回复

Both Promises and Observables provide abstractions to help us handle the asynchronous nature of applications. Günter and @Relu clearly point out the differences between them.

Since the code snippets are lengthy, let's use the examples below to understand them more clearly.

Thanks to Christoph Burgdorf's excellent article here


Angular uses Rx.js Observables instead of Promises for handling HTTP.

Suppose you are building a search functionality that displays results immediately as you type. This sounds familiar, but the task brings many challenges.

  • We do not want to access the server endpoint every time the user presses a key. It should not flood the server with numerous HTTP requests. Essentially, we only want to trigger it when the user stops typing, not on every keypress.
  • Do not use the same query parameters for subsequent requests to the search endpoint.
  • Handle unordered responses. When handling multiple requests simultaneously, we must consider the possibility of them returning in an unexpected order. Imagine we first type Computer, stop, a request is sent, then we type car, stop, another request is sent. Now we have two requests in progress. Unfortunately, the request carrying the Computer results returns after the request carrying the car results.

This demo only includes two files: app.ts and wikipedia-service.ts. However, in the real world, we would likely further split these components.


Below is a Promise-based implementation that does not handle any of the described edge cases.

wikipedia-service.ts

typescript
import { Injectable } from '@angular/core'; import { URLSearchParams, Jsonp } from '@angular/http'; @Injectable() export class WikipediaService { constructor(private jsonp: Jsonp) {} 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 }) .toPromise() .then((response) => response.json()[1]); } }

We are injecting the service to make a GET request to the Wikipedia API using Jsonp with the given search term. Note that we call toPromise() to convert the Observable<Response> to a Promise<Response>, and finally return a Promise<Array<string>>.

app.ts

typescript
// check the plnkr for the full list of imports import {...} from '...'; @Component({ selector: 'my-app', template: ` <div> <h2>Wikipedia Search</h2> <input #term type="text" (keyup)="search(term.value)"> <ul> <li *ngFor="let item of items">{{item}}</li> </ul> </div> ` }) export class AppComponent { items: Array<string>; constructor(private wikipediaService: WikipediaService) {} search(term) { this.wikipediaService.search(term) .then(items => this.items = items); } }

Here, there's not much surprise. We inject WikipediaService into the template and expose its functionality via the search method. The template simply binds to keyup and calls search(term.value).

We unwrap the Promise returned by the search method of WikipediaService and expose it as a simple string array to the template for us to loop through with *ngFor and build a list.

See the Promise-based implementation example on Plunker


Observables truly shine where

Let's change the code so that we no longer hit the endpoint on every keypress, but only when the user stops typing after a 400 ms pause

To reveal such superpowers, we first need to get an Observable<string> with the search term from user input. We can leverage Angular's formControl directive instead of manually binding to the keyup event. To use this directive, we first need to import ReactiveFormsModule into our application module.

app.ts

typescript
import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { JsonpModule } from '@angular/http'; import { ReactiveFormsModule } from '@angular/forms'; @NgModule({ imports: [BrowserModule, JsonpModule, ReactiveFormsModule] declarations: [AppComponent], bootstrap: [AppComponent] }) export class AppModule {}

After importing, we can use formControl in the template and set it to the name 'term'.

html
<input type="text" [formControl]="term"/>

In the component, we create a FormControl instance from @angular/forms and expose it as a field named 'term'.

Behind the scenes, term automatically exposes an Observable<string> as the valueChanges property. Now that we have an Observable<string>, we can overcome user input by calling debounceTime(400) on our Observable. This will return a new Observable<string> that emits a new value only when no new value appears within 400 ms.

typescript
export class App { items: Array<string>; term = new FormControl(); constructor(private wikipediaService: WikipediaService) { this.term.valueChanges .debounceTime(400) // wait for 400 ms pause in events .distinctUntilChanged() // ignore if next search term is same as previous .subscribe(term => this.wikipediaService.search(term).then(items => this.items = items)); } }

If our application already displays results, sending another search request wastes resources. To achieve the desired behavior, we simply call distinctUntilChanged immediately after debounceTime(400).

See the Observable implementation example on Plunker

For handling unordered responses, see the full article here

As for my use of HTTP in Angular, I agree that using Observables instead of Promises has no significant difference in normal use cases. These advantages are not really relevant in practice. I hope to see some advanced use cases in the future:)


Learn more

2024年6月29日 12:07 回复

你的答案