我正在努力了解rxJsflatMap
和之间的concatMap
区别。
我能理解的最明确的答案是,concatmap和flatmap之间的差异
所以我自己去尝试了一些事情。
import "./styles.css";
import { switchMap, flatMap, concatMap } from "rxjs/operators";
import { fromFetch } from "rxjs/fetch";
import { Observable } from "rxjs";
function createObs1() {
return new Observable<number>((subscriber) => {
setTimeout(() => {
subscriber.next(1);
subscriber.complete();
}, 900);
});
}
function createObs2() {
return new Observable<number>((subscriber) => {
setTimeout(() => {
subscriber.next(2);
//subscriber.next(22);
//subscriber.next(222);
subscriber.complete();
}, 800);
});
}
function createObs3() {
return new Observable<number>((subscriber) => {
setTimeout(() => {
subscriber.next(3);
//subscriber.next(33);
//subscriber.next(333);
subscriber.complete();
}, 700);
});
}
function createObs4() {
return new Observable<number>((subscriber) => {
setTimeout(() => {
subscriber.next(4);
subscriber.complete();
}, 600);
});
}
function createObs5() {
return new Observable<number>((subscriber) => {
setTimeout(() => {
subscriber.next(5);
subscriber.complete();
}, 500);
});
}
createObs1()
.pipe(
flatMap((resp) => {
console.log(resp);
return createObs2();
}),
flatMap((resp) => {
console.log(resp);
return createObs3();
}),
flatMap((resp) => {
console.log(resp);
return createObs4();
}),
flatMap((resp) => {
console.log(resp);
return createObs5();
})
)
.subscribe((resp) => console.log(resp));
console.log("hellooo");
我在这里用过那个操场的例子
问题
1)据我了解,flatMap的使用应混合使用输出,以便控制台日志类似于(1、3、2、4、5)。我已经尝试了30次以上,并且总是排在同一行(1、2、3、4、5)
我在做错什么还是在不懂得做错什么?
2)如果启用,createObs2()
然后createObs3()
删除注释,并在代码中包含多个发出的事件,那么事情将变得混乱。即使您更改为concatMap,它也会使事情变得混乱,并且结果会混杂在一起。我希望只有一次的多个数字会多次出现。结果可能是(1、2、33、3、2、22、3、33、4、5、4、3、4、5)为什么会发生这种情况?
我如何在操场上测试示例。我只从最后一个console.log(“ hello”)中删除了1个字母。仅观察到一次更改,例如console.log(“ heloo”),然后再次编译项目,并在控制台中输出输出。
编辑:我去flatMap和concatMap的原因是使用http库在angular中找到嵌套订阅的替代方法。
createObs1().subscribe( (resp1) => {
console.log(resp1);
createObs2().subscribe( (resp2) => {
console.log(resp2);
createObs3().subscribe( (resp3) => {
console.log(resp3);
createObs4().subscribe( (resp4) => {
console.log(resp4);
createObs5().subscribe( (resp5) => {
console.log(resp5);
})
})
})
})
})
您的测试方案确实不足以看到这两个运算符之间的差异。在您的测试用例中,每个可观察对象仅发出1次。如果一个observable只发出一个值,那么concatMap
and flatMap
(aka mergeMap)之间确实没有区别。仅当存在多个排放时才能看到差异。
因此,让我们使用另一种情况。让我们有一个source$
可观察的对象,它仅每1秒发出一个递增的整数。然后,在我们的“高阶映射运算符”(concatMap
&mergeMap
)中,我们将返回一个可观察对象,该对象每1秒发出一次可变的次数,然后完成。
// emit number every second
const source$ = interval(1000).pipe(map(n => n+1));
// helper to return observable that emits the provided number of times
function inner$(max: number, description: string): Observable<string> {
return interval(1000).pipe(
map(n => `[${description}: inner source ${max}] ${n+1}/${max}`),
take(max),
);
}
然后让我们基于source$
和定义两个单独的可观察对象inner$
。一种使用concatMap
,另一种使用flatMap
并观察输出。
const flatMap$ = source$.pipe(
flatMap(n => inner$(n, 'flatMap$'))
);
const concatMap$ = source$.pipe(
concatMap(n => inner$(n, 'concatMap$'))
);
在查看输出差异之前,让我们谈谈这些运算符的共同点。他俩:
不同之处在于它们如何创建和管理内部订阅:
concatMap
-一次只允许一个内部订阅。当它接收到发射时,一次只能订阅一个内部可观察到的信号。因此,它最初将订阅“排放1”创建的可观测对象,只有完成后,才订阅“排放2”创建的可观测对象。这与concat
静态方法的行为一致。
flatMap
(akamergeMap
)-允许许多内部订阅。因此,它将在收到新的排放量时订阅内部的可观测值。这意味着发射将不会按照任何特定的顺序进行,因为只要其任何内部可观察到的发射都将发射。这与merge
静态方法的行为是一致的(这就是为什么我个人更喜欢使用名称“ mergeMap”)。
这是一个StackBlitz,显示上述可观察物concatMap$
和的输出mergeMap$
:
希望以上说明有助于您解决问题!
#1-“使用flatMap应该混合输出”
之所以无法按您预期的那样工作,是因为只有一个发射正在通过flatMap
,这意味着您只有一个“内部可观察”的发射值。如上例所示,flatMap收到多个发射后,便可以具有多个独立发射的内部可观测对象。
#2-“ ...并且将代码包含多个发出的事件,那么事情就变得混乱了。 ”
“事情变得混乱”是由于具有多个内部订阅,这些订阅发出了值。
对于您提到的使用concatMap
并仍然获得“混合”输出的部分,我不希望如此。启用“自动保存”后,我在StackBlitz中看到了奇怪的行为,并观察到了发射现象(似乎有时它不能完全刷新,而旧的订阅似乎在自动刷新后仍然存在,这会带来非常混乱的控制台输出)。也许代码沙箱也有类似的问题。
#3-“我之所以去flatMap和concatMap的原因是使用http库找到了angular嵌套订阅的替代品”
这是有道理的。您不想弄乱嵌套的订阅,因为没有什么好方法可以保证内部订阅会被清除。
在大多数使用http调用的情况下,我发现这switchMap
是理想的选择,因为它将减少您不再关心的内部可观察对象的发射。想象一下,您有一个id
从路由参数读取的组件。它使用它id
进行http调用以获取数据。
itemId$ = this.activeRoute.params.pipe(
map(params => params['id']),
distinctUntilChanged()
);
item$ = this.itemId$.pipe(
switchMap(id => http.get(`${serverUrl}/items/${id}`)),
map(response => response.data)
);
我们只想item$
发出“当前项”(对应于url中的id)。假设我们的用户界面有一个按钮,用户可以单击该按钮导航至下一个项目,id
并且您的应用程序会发现自己是一个不断点击该按钮的点击满意用户,因此更改URL参数的速度甚至比http调用返回数据的速度更快。
如果选择mergeMap
,我们将最终获得许多内部可观察到的东西,这些可观察到的东西将发出所有这些HTTP调用的结果。充其量,当所有这些不同的呼叫返回时,屏幕将闪烁。最坏的情况下(如果调用无序),UI将显示与url中的ID不同步的数据:-(
如果选择concatMap
,则即使我们只在乎最近的一个,用户也将被迫等待所有的http调用完成。
但是,有了switchMap
,每当itemId
收到新的发射()时,它将取消订阅先前的内部可观察对象,并订阅新的。这意味着它将永远不会从不再相关的旧http调用中发出结果。:-)
有一点要注意的是,由于HTTP观测只能发出一次(不同运营商之间的选择switchMap
,mergeMap
,concatMap
)就显得不有所作为,因为他们都执行“内部观察的处理”对我们来说。但是,最好是将来对代码进行过时验证,并选择一个能够真正为您提供所需行为的代码,如果您开始收到的不止一个发射代码。
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句