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 all 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
74 changes: 72 additions & 2 deletions src/operator-docs/transformation/concatMap.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,76 @@
import { OperatorDoc } from '../operator.model';

export const concatMap: OperatorDoc = {
'name': 'concatMap',
'operatorType': 'transformation'
name: 'concatMap',
operatorType: 'transformation',
signature: `concatMap(project: Function, resultSelector?: Function): 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 then merged with the output observable
in a serialized fashion. The previous subscription must complete before the next begins.
<span class="informal">Inner observables are flatened 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+')'));
// concatMap's project function is executed just one time!
// Aditional cliks are not longer taking into account
// 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);
`,
externalLink: {
platform: 'JSBin',
url: 'https://stackblitz.com/edit/concatmap?file=index.ts'
}
}
],
relatedOperators: [
'concat',
'concatAll',
'concatMapTo',
'exhaustMap',
'mergeMap',
'switchMap'
]
};