Removing resultSelectors (WIP)#3304
Conversation
Generated by 🚫 dangerJS |
91b9dee to
99f21d4
Compare
|
@kwonoj that's done. |
|
So I wonder if there is a good way to deal with the result selector scenario other than just saying "tough luck". Going from something like the below just feels worse from a consumers perspective. I totally get it from the authors perspective. let a4: Rx.Observable<{ o: number; i: string; }> = o.mergeMap(x => [x.toString()], (o, i) => ({ o, i }), 3);to let a6: Rx.Observable<{ o: number; i: string; }> = o.map(o => Observable.from([o.toString()]).map(i => ({ o, i }))).mergeAll(3);I wonder if there is somewhere we can meet in the middle... I'll see if I can figure something out. |
|
all blocked by mono-repo roadmap, but what we discussed in general is probably considerable to have separate operator package for |
|
@david-driscoll the change would be more like: o.mergeMap((o => Observable.from(a.toString()).map(i => ({o, i })), 3);There's no need to use |
|
why are they superfluous ? Actually I can find ResultSelector pretty useful, because I can get access to the index of the source observable and inner observable. Also I can make a custom merge of the observables since I can get access to each item produce in each observable. Is there any place where I can get more info about this topic ? |
|
@luillyfe The reason they're seen as superfluous is due to the fact they complicate the inner workings of each operator they're running inside, where it causes problems unit testing all the parts of the operator. the result selector can easily substituted for something like... .mergeMap((outer, outerIndex) => Observable.from(outer).map((inner, innerIndex) => ...));However I agree that is pretty verbose and over the top, so I have a different PR where I'm proposing a helper method to help with the transition (and ensure consistency) #3332 // <type imports here>
import { map } from '../operators/map';
import { from } from '../observable/from';
export function over<O, I, R>(
selector: (outer: O, index: number) => ObservableInput<I>,
project: (outerValue: O, innerValue: I, outerIndex: number, innerIndex: number) => R): (outer: O, index: number) => Observable<R> {
return (outer, outerIndex) => from(selector(outer, outerIndex))
.pipe(map((inner, innerIndex) => project(outer, inner, outerIndex, innerIndex)));
}the usage is pretty simple... .mergeMap(over((outer, inner, outerIndex, innerIndex) => { ... }));The method is fairly simple but I'd hate to force everyone that wants to use it to define it, so I'll be bringing this up in the next meeting. |
|
@luillyfe they're superfluous because the following is equivalent: source$.pipe(
mergeMap((a, i) => of(a + i), (a, b, i, ii) => a + b + i + ii),
)is the same as: source$.pipe(
mergeMap((a, i) => of(a + i).pipe(map((b, ii) => a + b + i + ii)))
)Only the latter is actually better in many cases, as it allows for catching errors before they propagate out of the mergeMap operation. The added benefit here is size, as almost always people will be using the map operator, it's generally no extra cost, where we're removing several lines of code from the mergeMap operator. |
7a33063 to
de57152
Compare
|
Awesome, thank you guys @benlesh @david-driscoll for the clarification. |
BREAKING CHANGE: mergeMap, concatMap and concatMapTo no longer support a result selector, if you need to use a result selector, use the following pattern: `source.mergeMap(x => of(x + x).pipe(map(y => y + x))` (the pattern would be the same for `concatMap`).
- Implements mergeMapTo in terms of mergeMap - Removes resultSelector argument BREAKING CHANGE: `mergeMapTo` no longer accepts a resultSelector, to get this functionality, you'll want to use `mergeMap` and `map` together: `source.pipe(mergeMap(() => inner).pipe(map(y => x + y)))`
- removes resultSelector from forkJoin - updates tests
- removes resultSelector argument from `switchMap` and `switchMapTo` - updates tests BREAKING CHANGE: `switchMap` and `switchMapTo` no longer take `resultSelector` arguments, to get the same functionality use `switchMap` and `map` in combination: `source.pipe(switchMap(x => of(x + x).pipe(y => x + y)))`.
- Removes resultSelector argument - updates tests BREAKING CHANGE: `resultSelector` no longer supported, to get this functionality use: `source.pipe(exhaustMap(x => of(x + x).pipe(map(y => x + y))))`
- removes resultSelector argument - updates tests BREAKING CHANGE no longer supports `resultSelector` argument. The same functionality can be achieved by simply mapping either before or after `first` depending on your use case.
- Removes resultSelector argument - Updates tests BREAKING CHANGE: no longer accepts `resultSelector` argument. To get this same functionality, use `map`.
de57152 to
193ff36
Compare
spec/operators/concatMap-spec.ts
Outdated
| expectSubscriptions(e1.subscriptions).toBe(e1subs); | ||
| }); | ||
|
|
||
| <<<<<<< HEAD |
spec/operators/concatMap-spec.ts
Outdated
| }); | ||
|
|
||
| ======= | ||
| >>>>>>> feat(mergeMap|concatMap|concatMapTo): simplified the signatures |
- Also fixes issues from a bad rebase that I (@benlesh) did because I'm a dope.
193ff36 to
7ff121c
Compare
|
|
||
| function project() { return inner; } | ||
| function resultSelector(oV: string, iV: string, oI: number, iI: number) { return iV; } | ||
| const result = e1.mergeMap(project, resultSelector, 1); |
There was a problem hiding this comment.
It looks like these tests shouldn't have been removed, instead just removing the usage of resultSelector
|
|
||
| function project() { return inner; } | ||
| function resultSelector(oV: string, iV: string, oI: number, iI: number) { return iV; } | ||
| const result = e1.mergeMap(project, resultSelector, 2); |
|
|
||
| function project(x: string) { return inners[x]; } | ||
| function resultSelector(oV: string, iV: string, oI: number, iI: number) { return iV; } | ||
| const result = e1.mergeMap(project, resultSelector, 1); |
|
|
||
| function project(x: string) { return inners[x]; } | ||
| function resultSelector(oV: string, iV: string, oI: number, iI: number) { return iV; } | ||
| const result = e1.mergeMap(project, resultSelector, 2); |
| ' ^ !']; | ||
| const expected = '-----i---j---k---l-------i---j---k---l-------i---j---k---l---|'; | ||
|
|
||
| function resultSelector(oV: string, iV: string, oI: number, iI: number) { return iV; } |
There was a problem hiding this comment.
exists below "without resultSelector". I refactored to use the pipeable operator anyhow.
| ' ^ !']; | ||
| const expected = '-----i---j---(ki)(lj)k---(li)j---k---l---|'; | ||
|
|
||
| function resultSelector(oV: string, iV: string, oI: number, iI: number) { return iV; } |
There was a problem hiding this comment.
Oh... yeah, there's the same test below it "without resultSelector"
- also readds important tests removed in a previous commit in this PR
|
Thanks for the review @jayphelps! Note to future viewers of this PR: The red "X" was only because code coverage dropped 0.03% (or something like that)... So it's acceptable and merged anyhow. |
|
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. |
This is an experimental PR that is aiming to remove all superfluous resultSelector arguments from operators and observable creators.
To start with I've done
mergeMap,mergeMapTo,switchMap,switchMapTo,concatMap,concatMapToandforkJoin.See commit messages for more detail.
This is aimed at smaller size, simplifying the API, simplifying the code base, and simplifying TypeScript typings.
Just doing it makes it clear that it's a solid decision, as the code cleans up substantially everywhere it's done.
Relates to #2929