-
Notifications
You must be signed in to change notification settings - Fork 19
Why do some operators return Promises? #20
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
Comments
I guess it's just copying the TC39 proposal. Let me rephrase this question I think my confusion is why some instance methods return Promises vs Observables? Like |
Primarily because they only return one value. However it's reasonable to have them return observables, perhaps just not ergonomic in the common case. RxJS's versions of these methods return observables. |
I think it would be clearer for them to returns Observables, and there be some way to turn Observables into Promises in general, like with |
The two ways people generally convert an observable to a promise are:
The only gotcha is if the observable completes without emitting a value. In those cases, RxJS has found it's best to reject the returned promise with an error (in our case an But I'm amenable to having everything return observables, but providing two methods like |
Another option is methods that are named with My main feedback is I think it should be clear in a chain of |
I'm concerned about the readability. In RxJS, we export Although if we wanted, there could be a single method There are a lot of options: // 1. xValue() method.
await someObservable$.firstValue();
await someObservable$.lastValue();
// 2. xThen() method.
await someObservable$.firstThen();
await someObservable$.lastThen();
// 3. Static Observable methods
await Observable.firstValue(someObservable$);
await Observable.lastValue(someObservable$);
// 4. Static Promise methods
await Promise.firstValueFrom(someObservable$);
await Promise.firstValueFrom(asyncIterable);
await Promise.lastValueFrom(someObservable$);
await Promise.lastValueFrom(asyncIterable);
// 5. "at" (ala Array#at)
await someObservable$.at(0);
await someObservable$.at(-1); I have mixed feelings about all of these. Number 1 is the one I like the best. Mostly because it's easy to read, and there's some prior art in RxJS, which I'm used to. Number 5 is the most flexible, and has prior art in the language (that is arguably not well known, I still see new Number 4 is probably a no-go, I don't think I'd want to alter a common type like Finally, there was a "once upon a time" where the Observable completed with the last value, and was also "thennable", meaning calling That was scrapped because subscribing to an observable can have side effects, and it was concerned to be too confusing for folks that awaiting something could trigger a side effect, when with promises, the side effect was always underway prior to the await. |
Honestly, I was thinking about this and some of the same arguments could exist here that exist in the iterator-helpers proposal: Where The difference is here they have to be promises, because the result would come over an indeterminate amount of time. Because it's pushed at you. |
Relevantly, the current plan is for async iterator helpers (which are stage 2) to have Honestly I can't really imagine doing it another way. In that proposal, as in this one, As a user, why would you ever want an observable instead of a Promise here? |
The main reason is that it is synchronous, this means if Though I don't believe that most users would need the synchronous observation for most of the single value returning methods anyway. Why? Well the reason is simple, the other operators don't expose any value from calling e.g. Consider this example: // Also note by the time eventCount is set, all mousemove events have long since lost the
// opportunity to have preventDefault called as well
const eventCount = await div.on("mousemove").takeUntil(div.on("mouseup")).reduce((acc, event) => {
return acc + 1;
}, 0); the synchronous
Although this is how observables were presented even in the original TC39 proposal, I don't think they've ever really fitted this table properly, I would consider a more accurate table:
For the most part observable proponents argue that Though as mentioned above, the aggregate operators proposed don't really need the singular-value sync behaviour anyway, and basically all new host APIs tend to be explictly designed for promises, so I have minimal concern about the non-existence of a singular push-sync type. |
I think people may look at that long operator list and wonder whether this is the MVP list of operators or not.
So some justification for why this list of operators is the right one would be good.
The text was updated successfully, but these errors were encountered: