feat(publishReplay): add selector function to publishReplay#2885
feat(publishReplay): add selector function to publishReplay#2885benlesh merged 5 commits intoReactiveX:masterfrom
Conversation
Generated by 🚫 dangerJS |
benlesh
left a comment
There was a problem hiding this comment.
A change to the one test, and a request that more test coverage be added.
spec/operators/publishReplay-spec.ts
Outdated
| }); | ||
|
|
||
| it('should mirror a simple source Observable with selector', () => { | ||
| const selector = observable => observable.map(v => String.fromCharCode(96 + parseInt(v))); |
There was a problem hiding this comment.
this one is a little too clever. :)
Can you use something more straightforward? A hash lookup or something?
| expectObservable(published).toBe(expected); | ||
| expectSubscriptions(source.subscriptions).toBe(sourceSubs); | ||
| }); | ||
| }); |
There was a problem hiding this comment.
Need to add tests for:
- errors thrown in
selectordirectly. - when
selectorreturns an errored Observable (--a--b--#) - when
selectorreturns empty Observable - when
selectorreturns never - when
selectorreturns Observable.throw
|
Also, @martinsik, if you can explain your cool pixel art profile pic, that would be good too. What is it? |
benlesh
left a comment
There was a problem hiding this comment.
Last thing, we'll need to add function signature overrides for both operator/publishReplay and operators/publishReplay... they're slightly different. I've linked examples.
Thanks again!
src/operator/publishReplay.ts
Outdated
| * @method publishReplay | ||
| * @owner Observable | ||
| */ | ||
| export function publishReplay<T>(this: Observable<T>, bufferSize: number = Number.POSITIVE_INFINITY, |
There was a problem hiding this comment.
Like below, we'll need to add some function overloads for TypeScript users to keep this change from being breaking. Here's what the publish counterpart to this file is doing...
Lines 7 to 9 in accbcd0
src/operators/publishReplay.ts
Outdated
| @@ -7,6 +7,8 @@ import { UnaryFunction } from '../interfaces'; | |||
|
|
|||
| export function publishReplay<T>(bufferSize: number = Number.POSITIVE_INFINITY, | |||
There was a problem hiding this comment.
I think we'll need some function signature overloads for this so we don't break compatability for people expecting ConnectableObservable<T> to be the only return type...
See what publish is doing here for a better idea:
Lines 6 to 8 in accbcd0
|
LGTM.... @kwonoj? |
|
@benlesh Sorry, it's work in progress I just pushed it because It was too late yesterday :). I'm not sure about a few things there. btw, my avatar is a screenshot from this very old game https://en.wikipedia.org/wiki/Moonstone:_A_Hard_Days_Knight |
| const published = source.publishReplay(1, Number.POSITIVE_INFINITY, selector); | ||
|
|
||
| // The exception is thrown outside Rx chain (not as an error notification). | ||
| expect(() => published.subscribe()).to.throw(error); |
There was a problem hiding this comment.
This exception is thrown outside the chain so I need to check it like this. This situations isn't tested in neither publish nor multicast (if I didn't miss anything) so I'm not sure I'm doing it right.
There was a problem hiding this comment.
OH GEEZ! I missed this... So what should happen is you should catch the error and send it down the error path with observer.error(err). There are all sorts of examples of how to handle this around teh library.
Basically any user-supplied function needs to have it's errors caught and sent down the error channel of observation.
There was a problem hiding this comment.
But I would expect multicast to catch this error for me (for example like the map() operator does) because that's where the selector is called: https://github.com/ReactiveX/rxjs/blob/master/src/operators/multicast.ts#L64
| selectorOrScheduler?: IScheduler | OperatorFunction<T, R>, | ||
| scheduler?: IScheduler): Observable<R> | ConnectableObservable<R> { | ||
|
|
||
| return higherOrder(bufferSize, windowTime, selectorOrScheduler as any, scheduler)(this); |
There was a problem hiding this comment.
I couldn't find any other sane way to call higherOrder. TypeScript didn't allow me just using:
higherOrder(bufferSize, windowTime, selectorOrScheduler, scheduler)
.. but I think this should work.
There was a problem hiding this comment.
This will do, the other thing you can do is duplicate the logic that figures out what was passed from the other method.
There was a problem hiding this comment.
I understand, I didn't want to write the same logic twice so I'll leave it as it is.
| scheduler?: IScheduler): UnaryFunction<Observable<T>, ConnectableObservable<T>> { | ||
| return (source: Observable<T>) => multicast(new ReplaySubject<T>(bufferSize, windowTime, scheduler))(source) as ConnectableObservable<T>; | ||
| /* tslint:disable:max-line-length */ | ||
| export function publishReplay<T>(bufferSize?: number, windowTime?: number, scheduler?: IScheduler): UnaryFunction<Observable<T>, ConnectableObservable<T>>; |
There was a problem hiding this comment.
The publish operator defines MonoTypeOperatorFunction return type but I don't think that's correct because this results into Observable<T> while publish should return ConnectableObservable<T>.
https://github.com/ReactiveX/rxjs/blob/master/src/operators/publish.ts#L6-L8
There was a problem hiding this comment.
This is fine for now... we might need to address this more generally across the library.
| export function publishReplay<T, R>(bufferSize?: number, windowTime?: number, selector?: OperatorFunction<T, R>, scheduler?: IScheduler): OperatorFunction<T, R>; | ||
| /* tslint:enable:max-line-length */ | ||
|
|
||
| export function publishReplay<T, R>(bufferSize?: number, |
There was a problem hiding this comment.
I removed all default values here and left it up to ReplaySubject to decide.
benlesh
left a comment
There was a problem hiding this comment.
Add appropriate error handling.
| const published = source.publishReplay(1, Number.POSITIVE_INFINITY, selector); | ||
|
|
||
| // The exception is thrown outside Rx chain (not as an error notification). | ||
| expect(() => published.subscribe()).to.throw(error); |
There was a problem hiding this comment.
OH GEEZ! I missed this... So what should happen is you should catch the error and send it down the error path with observer.error(err). There are all sorts of examples of how to handle this around teh library.
Basically any user-supplied function needs to have it's errors caught and sent down the error channel of observation.
| selectorOrScheduler?: IScheduler | OperatorFunction<T, R>, | ||
| scheduler?: IScheduler): Observable<R> | ConnectableObservable<R> { | ||
|
|
||
| return higherOrder(bufferSize, windowTime, selectorOrScheduler as any, scheduler)(this); |
There was a problem hiding this comment.
This will do, the other thing you can do is duplicate the logic that figures out what was passed from the other method.
| scheduler?: IScheduler): UnaryFunction<Observable<T>, ConnectableObservable<T>> { | ||
| return (source: Observable<T>) => multicast(new ReplaySubject<T>(bufferSize, windowTime, scheduler))(source) as ConnectableObservable<T>; | ||
| /* tslint:disable:max-line-length */ | ||
| export function publishReplay<T>(bufferSize?: number, windowTime?: number, scheduler?: IScheduler): UnaryFunction<Observable<T>, ConnectableObservable<T>>; |
There was a problem hiding this comment.
This is fine for now... we might need to address this more generally across the library.
|
@benlesh I added a comment above that I'd expect |
|
Merged! Thanks @martinsik! |
|
This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs. |
Description:
This PR adds selector function to the
publishReplayoperator similarly topublish.I didn't modify any existing tests. However, this could be probably a breaking change because I added the parameter before
scheduler(I'm not sure this is a problem or not).Related issue (if exists):
#2844