cujojs / most

Ultra-high performance reactive programming
MIT License
3.5k stars 231 forks source link

Analog to `Rx.Observable.toPromise()` ? #471

Closed cloderic closed 6 years ago

cloderic commented 6 years ago

One of the nicest things about most is how well it plays with Promises. However unless I miss something there's only one way to extract values from the stream to a promise and that's stream.reduce. Obviously you can achieve everything you need with reduce but it can be hard to read and/or a pain to implement.

A common use case I have is doing some manipulation on a stream so it ends up with only one value using a combination of stream.filter, stream.take, most-buffer etc. and finishing that with an ugly .reduce((_, v) => v) which is not the most readable line.

Rx observables have a toPromise() function that actually resolves to the last event in the stream. I'm not a big fan as it is to really explicit as to which value is taken.

So here the proposal.

stream.retrieveFirst()

which is sugar for

stream.take(1).reduce((_, v) => v)

and

stream.retrieveLast()

which is sugar for

stream.reduce((_, v) => v)

What do you think?

briancavalier commented 6 years ago

Hey @cloderic, thanks for opening the discussion.

For your own usage, you can just wrap these up in functions, and then just call them, or use them with thru.

const retrieveLast = stream => stream.reduce((_, v) => v)

const retrieveFirst = stream => retrieveLast(stream.take(1))

For the larger community, I think this could be a nice mostjs-community package. Is that something you'd be interested in contributing?

Cheers!

cloderic commented 6 years ago

These aren't actually usable with thru because they don't return a stream but a promise (unless I'm missing something). which they'll be quite a pain to use if not in the main package.

In any case IMHO methods like that are the missing link that enable a full interrop between most and Promises, they should be in the core package.

one other possibility would be to adapt drain() so that it returns the last value of the stream (thus becoming my retrieveLast, which is the most important one).

cloderic commented 6 years ago

Just had another idea,

Maybe drain() should be able to take a Stream => Promise optional function acting as an equivalent of thru() for stream to Promise packages that'd be as usable as the multiple stream transformation packages.

briancavalier commented 6 years ago

These aren't actually usable with thru because they don't return a stream

Hmmm, good point. The docs clearly say the function must be Stream -> Stream. However, looking at and thinking about thru more carefully in this promise-returning light, I don't think there's any real reason it shouldn't be possible.

Besides the docs, the reason it won't work atm (in TypeScript at least) is that thru's TS type def restricts the return type. I think what we need to do is make the type definition fully parametric in its return type:

  thru<B>(transform: (stream: Stream<A>) => B): B;

Then it'll happily admit promise-returning functions while remaining type safe. It also appears to be backward compatible, so we could do it in a minor release.

Thoughts?

cloderic commented 6 years ago

IMO it'd works fine, just have to make sure it's clear in the documentation and in the typescript definitions (I can submit a PR for both if you'd like).

I'll happily write my helpers for these use case like I did with most-buffer and most-limiter.

briancavalier commented 6 years ago

Cool. I started it in https://github.com/cujojs/most/pull/473. It'd be great to get your feedback there, especially on the docs.

I'll happily write my helpers for these use case like I did with most-buffer and most-limiter.

:+1: Thanks!

cloderic commented 6 years ago

And there it is, the brand new most-nth

briancavalier commented 6 years ago

@cloderic Awesome, and we released most 1.6.0 with the thru return type update.