我有三个可观察物foo$
,bar$
和baz$
,我将它们合并在一起以形成另一个可观察物。可以按预期运行:
>>>
<<<
const foo$ = of('foo');
const bar$ = of('bar');
const baz$ = of('baz');
merge(foo$, bar$, baz$).pipe(startWith('>>>'), endWith('<<<')).subscribe(str => {
console.log(str);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/6.5.2/rxjs.umd.min.js"></script>
<script>const {merge, of} = rxjs; const {startWith, endWith} = rxjs.operators;</script>
现在,如果以上三个可观察值均未发出值,则我既不想输出>>>
也不会输出<<<
。因此,startWith
和endWith
仅在merge(foo$, bar$, baz$)
实际发出值的情况下才能“运行”。
为了模拟我使用过滤功能拒绝了foo$
,bar$
和baz$
发出的所有值。
const foo$ = of('foo').pipe(filter(() => false));
const bar$ = of('bar').pipe(filter(() => false));
const baz$ = of('baz').pipe(filter(() => false));
merge(foo$, bar$, baz$).pipe(startWith('>>>'), endWith('<<<')).subscribe(str => {
console.log(str);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/6.5.2/rxjs.umd.min.js"></script>
<script>const {merge, of} = rxjs; const {startWith, endWith, filter} = rxjs.operators</script>
尽管您在输出中看到,startWith
和endWith
都发出了它们的值,即使merge()
没有产生任何值。
问题:如果可观察对象没有发出单个值,如何阻止startWith
和endWith
执行?
答案 0 :(得分:2)
第一个条件很简单。您可以在第一次发射之前加上concatMap
:
mergeMap((v, index) => index === 0 ? of('>>>', v) : of(v))
第二种情况比较棘手。您基本上希望与defaultIfEmpty
相反的右边。我想不出任何简单的解决方案,因此无论如何我都可能会使用endWith
,如果它是第一个也是唯一的发射,则可以忽略发射(这意味着源刚刚完成而没有发射任何东西):
endWith('<<<'),
filter((v, index) => index !== 0 || v !== '<<<'),
完整示例:
const foo$ = of('foo');//.pipe(filter(() => false));
const bar$ = of('bar');//).pipe(filter(() => false));
const baz$ = of('baz');//.pipe(filter(() => false));
merge(foo$, bar$, baz$).pipe(
mergeMap((v, index) => index === 0 ? of('>>>', v) : of(v)),
endWith('<<<'),
filter((v, index) => index !== 0 || v !== '<<<'),
).subscribe(console.log);
答案 1 :(得分:1)
摘自startWith
的文档:
返回一个Observable,它在开始发射源Observable发出的项目之前先发出您指定为参数的项目。
因此startWith
和endWith
将始终运行。
我不知道您的预期结果应该是什么,但是如果您只想为每个发射值连接字符串,则可以使用map
或switchMap
运算符。
编辑:
map
用来连接每个值的示例:
const foo$ = of('foo').pipe(filter(() => false));
const bar$ = of('bar').pipe(filter(() => false));
const baz$ = of('baz').pipe(filter(() => false));
merge(foo$, bar$, baz$).pipe(map(v => `>>>${v}<<<`)).subscribe(str => {
console.log(str);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/6.5.2/rxjs.umd.min.js"></script>
<script>const {merge, of} = rxjs; const {startWith, endWith, filter, map} = rxjs.operators</script>
答案 2 :(得分:1)
您可以将合并的observable分配给变量,从流中获取第一个元素,然后在发出至少一个值时映射到要执行的observable。
const foo$ = of('foo').pipe(filter(() => false))
const bar$ = of('bar').pipe(filter(() => false))
const baz$ = of('baz').pipe(filter(() => false))
const merged$ = merge(foo$, bar$, baz$);
merged$.pipe(
take(1),
switchMap(() => merged$.pipe(
startWith('>>>'),
endWith('<<<')
))
).subscribe(console.log);
答案 3 :(得分:0)
一种实现方法是使用 materialize-dematerialize operators 对:
source$.pipe(
// turn all events on stream into Notifications
materialize(),
// wrap elements only if they are present
switchMap((event, index) => {
// if its first event and its a value
if (index === 0 && event.kind === 'N') {
const startingNotification = new Notification('N', '>>>', undefined);
return of(startingNotification, event);
}
// if its a completion event and it not a first event
if (index > 0 && event.kind === 'C') {
const endingNotification = new Notification('N', '<<<', undefined);
return of(endingNotification, event);
}
return of(event);
}),
// turn Notifications back to events on stream
dematerialize()
)
在操场上玩这段代码:
https://thinkrx.io/gist/5a7a7f1338737e452ff6a1937b5fe05a
为了方便起见,我还在其中添加了一个空的错误源
希望这会有所帮助
答案 4 :(得分:0)
我看到所有答案都是结合运算符完成的,但是仅使用运算符来完成此解决方案有点棘手,为什么不创建自己的可观察项?我认为此解决方案是最容易理解的。 没有隐藏的聪明之处,没有复杂的运营商组合,没有订阅重复...
解决方案:
const foo$ = of('foo')//.pipe(filter(() => false));
const bar$ = of('bar')//.pipe(filter(() => false));
const baz$ = of('baz')//.pipe(filter(() => false));
function wrapIfOnEmit$(...obs) {
return new Observable(observer => {
let hasEmitted;
const subscription = merge(...obs).subscribe((data) => {
if (!hasEmitted) {
observer.next('>>>');
hasEmitted = true;
}
observer.next(data);
},
(error) => observer.error(error),
() => {
if (hasEmitted) observer.next('<<<');
observer.complete();
})
return () => subscription.unsubscribe();
})
}
wrapIfOnEmit$(foo$, bar$, baz$).subscribe(console.log);
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/6.5.2/rxjs.umd.min.js"></script>
<script>const {merge, of, Observable} = rxjs; const {filter} = rxjs.operators</script>
希望这会有所帮助!