Skip to content
This repository has been archived by the owner on Oct 1, 2018. It is now read-only.

docs(operators): add documentation for concatMap #272

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 74 additions & 2 deletions src/operator-docs/transformation/concatMap.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,78 @@
import { OperatorDoc } from '../operator.model';

export const concatMap: OperatorDoc = {
'name': 'concatMap',
'operatorType': 'transformation'
name: 'concatMap',
operatorType: 'transformation',
signature: `concatMap(project: (value: T, index: number) => ObservableInput<I>,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we are going without generic signature, correct?

Copy link
Contributor Author

@luillyfe luillyfe Feb 23, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am changing to the not generic version. 👍

?resultSelector: (outerValue: T, innerValue: I, outerIndex: number, innerIndex: number) => R): Observable`,
parameters: [
{
name: 'project',
type: 'function(value: T, index: number): ObservableInput',
attribute: '',
description: `A function that, when applied to an item emitted by the source
Observable, returns an Observable.`
},
{
name: 'resultSelector',
type:
'function(outerValue: T, innerValue: I, outerIndex: number, innerIndex: number): any',
attribute: 'optional',
description: `A function to produce the value on the output Observable based on the values
and the indices of the source (outer) emission and the inner Observable
emission. The arguments passed to this function are:
- 'outerValue': the value that came from the source.
- 'innerValue': the value that came from the projected Observable.
- 'outerIndex': the "index" of the value that came from the source.
- 'innerIndex': the "index" of the value from the projected Observable.`
}
],
marbleUrl: 'http://reactivex.io/rxjs/img/concatMap.png',
shortDescription: {
description: `Project each source value to an Observable which is merge in the output observable,
in a serialized fashion waiting for each one to complete before merging the next one
<span class="informal">Maps each value to an Observable, then flattens all of
these inner Observables using <a href="#/operators/concatAll">concatAll</a>.</span>`
},
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a few updates:

Project each source value to an Observable which is then merged with the output observable in a serialized fashion. The previous subscription must complete before the next begins. Inner Observables are flattened using <a href="#/operators/concatAll">concatAll</a>.</span>

walkthrough: {
description: `the source observable maps values to inner observable, subscribe and emit in order.
After subscribing the source observable is ended therefore the concatMap's project function
is only executed once. the second parameter 'resultFunction', allows you to access to the index of
source observable and inner observable (besides the items)`
},
examples: [
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm wondering if a better starting example for concatMap would be showing delayed observables executing in order, after completion. Generally, questions I see about concatMap are about why you would want to use this vs mergeMap or switchMap. I think we need to emphasize the difference between these three in the primary examples. What do you all think? @sumitarora @ashwin-sureshkumar @ladyleet

Copy link
Contributor Author

@luillyfe luillyfe Feb 23, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But I think this example could works to prove the singularity of concatMap:
with concatMap the project Function is executed just one time:
Click(0), Interval(0) -> Click(0), Interval(1) -> Click(0), Interval(2) etc

with mergeMap the project Function is executed every time I make a click (it could produce memory leaks):
Click(0), Interval(0) -> (click) Click(0), Interval(1) - Click(1), Interval(0) -> etc

with switchMap the project Function is executed every time I make a click but it ends the active subscription until switch to another observable:
Click(0), Interval(1) -> (click) Click(1), Interval(0) -> (click) Click(2), Interval(0)

Please check the live example, https://stackblitz.com/edit/concatmap?file=index.ts

Copy link

@jsonberry jsonberry Feb 27, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm 🤔 about this one. I think this example shows how concatMap works, but I'm not sure it would clearly show it's use versus mergeMap or switchMap, to a novice, like @btroncone mentioned.

The first time concatMap's use became apparent to me was when I saw ajax results being returned in order of pagination. I tried switching to a mergeMap and saw results returning out of order, because some paginated results were able to return faster than others. That's when concatMap clicked for me, as well as concurrency with mergeMap.

The example also uses a resultSelector function, and it looks like those are probably going to get the axe: ReactiveX/rxjs#3304 It might be best to omit them from examples on the docs here.

{
name:
'Map the first click to inner observable (it ended the Observable of clicks)',
code: `
import { Observable } from 'rxjs/Observable';
import { interval } from 'rxjs/observable/interval';
import { fromEvent } from 'rxjs/observable/fromEvent';

import { mapTo, concatMap } from 'rxjs/operators';

const $click = fromEvent(document, 'click');
const $interval = interval(3000)
.pipe(mapTo((iClick, iInterval) => 'Click('+iClick+')'+', '+'Interval('+iInterval+')'));
// the ConcatMap's project function is executed just one time!
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would update to:
concatMap's project function is executed just one time!

// Even if you make aditional cliks the ConcatMap's project function is not longer executed
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Small typo, additional, clicks, no longer

// output; Click(0), Interval(0) -> Click(0), Interval(1) -> Click(0), Interval(2) -> etc
$click.pipe(concatMap(() => $interval,
(fromSource, fromInterval, indexSource, indexInterval) => fromInterval(indexSource, indexInterval)))
.subscribe(console.log);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi, could you add the expected output. Maybe take a look at the documentation guidelines. There are some tipps and tricks for documenting operators. Thanks for PR!!!

`,
externalLink: {
platform: 'JSBin',
url: 'https://stackblitz.com/edit/concatmap?file=index.ts'
}
}
],
relatedOperators: [
'concat',
'concatAll',
'concatMapTo',
'exhaustMap',
'mergeMap',
'switchMap'
]
};