mergeMap
の目的がまったくわかりません。 2つの「説明」を聞いたことがあります。
merge
とmap
の組み合わせです-いいえ(または、これを複製できません)。次のコードを検討してください。
var obs1 = new Rx.Observable.interval(1000);
var obs2 = new Rx.Observable.interval(1000);
//Just a merge and a map, works fine
obs1.merge(obs2).map(x=> x+'a').subscribe(
next => console.log(next)
)
//Who know what - seems to do the same thing as a plain map on 1 observable
obs1.mergeMap(val => Rx.Observable.of(val + `B`))
.subscribe(
next => console.log(next)
)
「Who knows what」というラベルの付いた最後の部分は、obs1
上のマップにすぎません-ポイントは何ですか?
mergeMap
は実際に何をしますか?有効なユースケースの例は何ですか? (できればいくつかのコードで)
tl; dr;mergeMap
は、map
よりもはるかに強力です。 mergeMap
を理解することは、Rxの全機能にアクセスするために必要な条件です。
mergeMap
とmap
の両方が単一のストリームに作用します(対Zip
、combineLatest
)
mergeMap
とmap
の両方がストリームの要素を変換できます(vs. filter
、delay
)
ソースストリームのサイズを変更することはできません(仮定:map
自体はthrow
ではありません);ソースからの各要素に対して、厳密に1つのmapped
要素が発行されます。 map
は要素を無視できません(たとえばfilter
など)。
デフォルトのスケジューラの場合、変換は同期的に行われます。 100%クリア:ソースストリームはその要素を非同期に配信できますが、次の各要素はすぐにmapped
になり、さらに再送信されます。 map
は、たとえばdelay
のように時間内に要素をシフトできません
戻り値に制限はありません
id
:x => x
ソースストリームのサイズを変更できます。各要素には、任意の数(0、1、または多く)の新しい要素が作成/放出される場合があります
非同期性を完全に制御できます-新しい要素が作成/放出されるときと、ソースストリームから同時に処理される要素の数の両方。たとえば、ソースストリームが10個の要素を放出したが、maxConcurrency
が2に設定されている場合、最初の2つの要素がすぐに処理され、残りの8つはバッファリングされます。処理されたcomplete
dの1つが処理されると、ソースストリームの次の要素などが処理されます-少し注意が必要ですが、以下の例を見てください
他のすべての演算子は、単にmergeMap
およびObservable
コンストラクターで実装できます。
再帰的な非同期操作に使用できます
戻り値はObservable型である必要があります(または、Rxはそれからobservableを作成する方法を知っている必要があります-例えば、promise、array
id
:x => Rx.Observable.of(x)
let array = [1,2,3]
fn map mergeMap
x => x*x [1,4,9] error /*expects array as return value*/
x => [x,x*x] [[1,1],[2,4],[3,9]] [1,1,2,4,3,9]
アナロジーは全体像を示しておらず、基本的に.mergeMap
に対応し、maxConcurrency
が1に設定されています。このような場合、要素は上記のように順序付けられますが、通常はそうである必要はありません。私たちが持っている唯一の保証は、新しい要素の放出は、基礎となるストリーム内のそれらの位置による順序であることです。例:[3,1,2,4,9,1]
と[2,3,1,1,9,4]
は有効ですが、[1,1,4,2,3,9]
は有効ではありません(4
が基になるストリームで2
の後に発行されたため)。
mergeMap
を使用したいくつかの例:// implement .map with .mergeMap
Rx.Observable.prototype.mapWithMergeMap = function(mapFn) {
return this.mergeMap(x => Rx.Observable.of(mapFn(x)));
}
Rx.Observable.range(1, 3)
.mapWithMergeMap(x => x * x)
.subscribe(x => console.log('mapWithMergeMap', x))
// implement .filter with .mergeMap
Rx.Observable.prototype.filterWithMergeMap = function(filterFn) {
return this.mergeMap(x =>
filterFn(x) ?
Rx.Observable.of(x) :
Rx.Observable.empty()); // return no element
}
Rx.Observable.range(1, 3)
.filterWithMergeMap(x => x === 3)
.subscribe(x => console.log('filterWithMergeMap', x))
// implement .delay with .mergeMap
Rx.Observable.prototype.delayWithMergeMap = function(delayMs) {
return this.mergeMap(x =>
Rx.Observable.create(obs => {
// setTimeout is naive - one should use scheduler instead
const token = setTimeout(() => {
obs.next(x);
obs.complete();
}, delayMs)
return () => clearTimeout(token);
}))
}
Rx.Observable.range(1, 3)
.delayWithMergeMap(500)
.take(2)
.subscribe(x => console.log('delayWithMergeMap', x))
// recursive count
const count = (from, to, interval) => {
if (from > to) return Rx.Observable.empty();
return Rx.Observable.timer(interval)
.mergeMap(() =>
count(from + 1, to, interval)
.startWith(from))
}
count(1, 3, 1000).subscribe(x => console.log('count', x))
// just an example of bit different implementation with no returns
const countMoreRxWay = (from, to, interval) =>
Rx.Observable.if(
() => from > to,
Rx.Observable.empty(),
Rx.Observable.timer(interval)
.mergeMap(() => countMoreRxWay(from + 1, to, interval)
.startWith(from)))
const maxConcurrencyExample = () =>
Rx.Observable.range(1,7)
.do(x => console.log('emitted', x))
.mergeMap(x => Rx.Observable.timer(1000).mapTo(x), 2)
.do(x => console.log('processed', x))
.subscribe()
setTimeout(maxConcurrencyExample, 3100)
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.1.1/Rx.min.js"></script>
.mergeMap()
を使用すると、高次のObservableを単一のストリームにフラット化できます。例えば:
Rx.Observable.from([1,2,3,4])
.map(i => getFreshApiData())
.subscribe(val => console.log('regular map result: ' + val));
//vs
Rx.Observable.from([1,2,3,4])
.mergeMap(i => getFreshApiData())
.subscribe(val => console.log('mergeMap result: ' + val));
function getFreshApiData() {
return Rx.Observable.of('retrieved new data')
.delay(1000);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.1.0/Rx.js"></script>
.xxxMap()
演算子の詳細な説明については、この他の質問の私の答えを参照してください: Rxjs-配列内の複数の値を抽出し、それらを観測可能なストリームにフィードバックするには同期的に