Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add "single" as alternative for observables that only emit once or error and then complete #5273

Open
NilsEngelbach opened this issue Jan 29, 2020 · 14 comments

Comments

@NilsEngelbach
Copy link

Feature Request

A special type for observable that only emits one value or throws error and then completes.

Is your feature request related to a problem? Please describe.
The Angular HTTPClient returns observables for http requests, but you can be sure they will complete after one emission/error. I already saw people wrapping those observables in a Promise just to "make sure / emphasis" it will emit once (by the return type Promise). As trade off you loose the nice rxjs observables api (pipes etc.).

Describe the solution you'd like
The Java Implementation of Rx provides a special type for this use case called "Single".
http://reactivex.io/documentation/single.html
I know Java might not be the best source for inspiration, but in this case the additional type could provide useful additonal type information for consumers. There are things that "by nature" do not return a stream of multiple values but only one e.g. a http request.

The first sentence in rxjs documentation on observables states:

Observables are lazy Push collections of multiple values.

As beginner you can easily get confused by a http request returning an observable and therefore somehow emitting multiple values (multiple = more than one).

I can understand that such "Single" type could on the other hand cause some trouble for beginners because it seems to be something similar to a Promise. But imho it would be nice to have such a type with a subset of rxjs operators you already know from observables. The subset of operators would not include the operators that depend on the emission of multiple values or don't make sense for a single value (take, pairwise, ...).

Describe alternatives you've considered
If a new type like "Single" can not be taken into consideration, at least the documentation should be adjusted to clarify that observables can also emit 0 or only 1 value.

Additional context
What would be the reasons against having a new type of observables like single?

  • More complex to understand for beginners?
  • Bigger footprint of the library?
  • Confusion with existing promise implementation?
  • What else?

There were already discussions about similar topics in #2469 and #3424 but I couldn't find reasonable arguments against it, yet.

@NilsEngelbach NilsEngelbach changed the title Add "single" as alternative for observables that only emit once or error and than complete Add "single" as alternative for observables that only emit once or error and then complete Jan 29, 2020
@cartant
Copy link
Collaborator

cartant commented Jan 29, 2020

IMO, this is something that ought to be expressible with TypeScript. And my understanding is that there is some interest in this happening. I don't think there is any appetite for a runtime representation (e.g. a class hierarchy) for these sorts of observable behaviours. Personally, I would not be in favour of a runtime representation.

Mechanisms for augmenting the TypeScript declarations to identify observables that complete, etc. is being given some consideration. However, it is not at the top of the priority list. The top priority, ATM, is v7 - in which the package will be brought up to a more recent minimum version of TypeScript and the package's type signatures will be improved.

@NilsEngelbach
Copy link
Author

Is there some documentation about what was already considered exactly for augmenting the TypeScript declarations? Do you think this could be handled completly by augmenting the TypeScript declarations?

From consumer point of view it still would be a new "type" that would require addtional documentation etc., correct?

@cartant
Copy link
Collaborator

cartant commented Jan 29, 2020

Is there some documentation ... ?

No. It's literally just an idea, ATM.

@jgbpercy
Copy link

There's this discussion/proposal, which I believe would allow this to be expressed in TypeScript #5042

From that issue:

This technique would also give us a Single for free since fromFetch(), from(promise), and generated API could be set this hidden tuple to a single element. This can also remove the need for folks to do things like api.fetchData().pipe(take(1)).

@kwonoj
Copy link
Member

kwonoj commented Jan 29, 2020

Previous issues were mostly about not discussing necessary of this feature or not, but more of if this should be in core lib or not. I still stands for this would go in userland, either if it's runtime or type time interfaces.

@NilsEngelbach
Copy link
Author

@kwonoj what are the main reasons for you against such a type/feature in rxjs, besides the ones I already mentioned?
What would be good criteria to judge if something is necessary or useful?

@kwonoj
Copy link
Member

kwonoj commented Jan 29, 2020

We strongly encourage to land new features / or operators / etcs into userland first to see if it's being widely used, discussing potential api surface changes, revealing edge cases, etcs then would like to discuss include in core as next step. It is nearly not possible to deprecate something landed in core, and even small modification can cause wide effect once it's landed.

So question we always ask is flipped: is there reason it can't be userland that user opt-in to install to use it? I don't see reason why this can't be userland implementation.

@tmtron
Copy link

tmtron commented Nov 19, 2022

is there reason it can't be userland that user opt-in to install to use it? I don't see reason why this can't be userland implementation.

Single/Maybe have different contracts than Observable and thus many types in the library would need to be changed.
e.g. the return type of these operators: first, last, toArray, etc. should be Single
I don't see how this could be done in a user-land library.

e.g. Quote from the RxJava docs for Single:

it can only emit either a single successful value or an error (there is no "onComplete" notification as there is for an Observable)

The Single operates with the following sequential protocol:
onSubscribe (onSuccess | onError)?
Note that onSuccess and onError are mutually exclusive events; unlike Observable, onSuccess is never followed by onError.

On the other hand I also don't know how this could easily be added in a type-safe way to RxJs: i.e. what would the pipe method and all it's overloads look like, when Single, Maybe and Observable can be used?

So, I understand that the RxJs authors are reluctant to introduce this feature. It would for sure be a lot of work and a major change.

@ynoplanetashka2
Copy link

in fact because Single's contract differ from Observable's contract there are some operations which are valid only applied to Single. for example, if we want to create operator similar to Promise.any for Observable's then we would find out that Observable contract isn't sufficient to create valid analogue for Promise.any. why? because for meaningful implementation of Promise.any analogue we have to know whenever Observable will reject or not at it's first emit.
consider we applied operator 'any' to observableA and observableB and suppose we have such marble diagrams:
observableA: --0--X
observableB: ---------0
we should mirror first observable which will not throw, but at the moment of observableA first emit we can't know will it throw or not, so we don't know should we mirror it's values or not.
such problem doesn't appear with Single, because by it's contract we can be sure to know will it throw or not at the moment of first(and only) emit.
and if we would implement Promise.any analogue operator it should probably work only with Single and probably throw for Observable.
and btw seeing something like this is always confusing

// will this function perform chunking?
// if no users found will it emit empty array or eventually complete without emitting?
getUsers(): Observable<User[]>
// that would be really cool to see something like this instead
getUsers(): Single<User[]>

@drew-mcclelland-ch
Copy link

Pretty bummed this hasn't been implemented yet. My company is building a new webservice using NestJS whose HTTP client returns all API requests as Observables. Coming from a strong Scala background, I got super excited to leverage Observables and loved the reading the documentation of the API.

However, I'm pretty disheartened that you have to treat single values the same as a stream. From my familiarity with Akka, I definitely see why. But I guess I was hoping Observables would be a replacement for JS Promise because they're returned from an HTTP API. It's clear they are not.

IMO there definitely needs to be a way to differentiate between a single value, and a stream of values, if major libraries like Angular and NestJS are going to leverage them for API calls.

It feels so dirty doing a firstValueFrom and discarding the rest of the stream for every single HTTP endpoint.

A few questions if someone can humor me:

  1. Are Angular and NestJS devs really okay with always pulling single values from these Observables and discarding the remainder? Without any documentation from the type system that this is okay to do?

  2. Are there no "userland" libraries that support this functionality or add these types?

  3. Has anyone attempted wrapping something like NestJS's HTTP client in something that returns a Single type, similar to what's described in this article? I realize this would solely be for documentation purposes, but I think it's important for unfamiliar developers to know when it's safe to only pull a single value out of an observable.

Definitely grasping at straws here, because the alternative is to just rely on Axios directly and deal with Promise's.

@jgbpercy
Copy link

  1. Are Angular and NestJS devs really okay with always pulling single values from these Observables

Angular dev here... I think when I started following this issue ~5 (🤯) years ago I had a similar feeling to you, but I would have to say that this hasn't really been a major annoyance or source of bugs over the past few years of writing non-trivial Angular front-ends.

@alexanderharding
Copy link

I'm late to the discussion and I'm bound to miss context from previous comments.

I'd be interested to see what the Angular team has to say about this. HttpClient requests can be a stream of data (HttpEvents), it just so happens to be it's default behavior observes just the response event. I could see why they just default to Observable so logic can be simpler (vs adding a function overload for Promise as the default). I understand this is not a request for Angular to change their code though so I can only suggest what my team is doing. Even though we know the observable will complete, we conform the observable contact and either use take(1) or firstValueFrom depending on our use case. This seems fine enough to me. Otherwise we can just trust that the Angular implementation is working as expected.

@OliverJAsh
Copy link
Contributor

OliverJAsh commented Jan 16, 2025

One of the biggest problems with using Observables for singular async values is that first/last/firstValueFrom/lastValueFrom are unsafe because they can result in an error if the source observable is mistakingly empty (emits 0 items then completes).

A type designed specifically for the use case of singular async values would solve this problem. It would provide a typesafe guarantee that there will be exactly one value—no more and no less. So there will never be an error at runtime.

Using Observables for everything async is a bit like using Arrays for everything sync.

I haven't used either of these very much but I wanted to mention them in case they're helpful to others:

  • Effect. The Effect type models a single async value. Effect also defines a Stream type for modelling multiple async values.
  • Fluture. Seems to be unmaintained now though.

@drew-mcclelland-ch
Copy link

@alexanderharding Yeah that makes sense. I'm coming from being exposed to Observables from NestJS's HttpModule in a backend service. In this case, I would think 99% of the time you'd make a single HTTP request to retrieve some data from another service, so the Observable will only hold one value

HttpClient requests can be a stream of data (HttpEvents), it just so happens to be it's default behavior observes just the response event.

Out of curiosity, in the Angular world is it super common to pull streams of data in this way? More common than pulling data from discrete API calls? I've worked full stack off and on for a while and still have yet to use a streaming API call, and I'm sure they have their uses, but they seem relatively rare compared to standard API requests

I'm curious if Angular has functionality similar to other FE frameworks that continuously auto-refresh data by making api calls on a timer? In that case it would make sense why Angular devs used an Observable for their HttpClient by default

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

9 participants