这里是要求:
单击“开始”按钮时,每100毫秒发出一次事件x,每次发出都对应一个UI更新。当x次发射完成时,它将触发最终的UI更新,看起来很简单吧?
这是我的代码:
const start$ = fromEvent(document.getElementById('start'), 'click')
const intervel$ = interval(100)
.pipe(
take(x),
share()
)
var startLight$ = start$
.pipe(
switchMap(() => {
intervel$
.pipe(last())
.subscribe(() => {
// Update UI
})
return intervel$
}),
share()
)
startLight$
.subscribe(function (e) {
//Update UI
})
很明显,在switchMap
内进行订阅是反模式的,因此我尝试重构代码:
const startInterval$ = start$
.pipe(
switchMapTo(intervel$),
)
startInterval$.pipe(last())
.subscribe(() => {
//NEVER Receive value
})
const startLight$ = startInterval$.pipe(share())
问题在于intervel $流是在switchMap
内部生成的,无法在外部访问,您只能访问生成interval $的流,即start $,它永远不会完成!
是否有更聪明的方法来处理此类问题,或者这是rxjs
的固有局限性?
答案 0 :(得分:1)
将您的更新逻辑放入switchMap
和tap()
内,点击将运行多次,并且subscribe()仅获取最后一个发射
const startInterval$ = start$
.pipe(
switchMap(()=>intervel$.pipe(tap(()=>//update UI),last()),
)
startInterval$
.subscribe(() => {
// will run one time
})
答案 1 :(得分:1)
您非常亲密。在intervel $中使用last()仅将最后一个发送给下面的订阅。工作StackBlitz。以下是StackBlitz的详细信息:
const start$ = fromEvent(document.getElementById('start'), 'click');
const intervel$ = interval(100)
.pipe(
tap(() => console.log('update UI')), // Update UI here
take(x),
last()
);
const startInterval$ = start$
.pipe( switchMapTo(intervel$));
startInterval$
.subscribe(() => {
console.log('will run once');
});
如果您不希望使用tap()
,则只需通过仅获取第一个发射,然后完成take(1)
或first()
即可使start $完成。 Here is a new StackBlitz显示了这一点。
const start$ = fromEvent(document.getElementById('start'), 'click')
.pipe(
first()
);
const intervel$ = interval(100)
.pipe(
take(x)
);
const startInterval$ = start$
.pipe(
switchMapTo(intervel$)
);
startInterval$
.subscribe(
() => console.log('Update UI'),
err => console.log('Error ', err),
() => console.log('Run once at the end')
);
此方法(或完成Observable的任何方法)的缺点是,一旦完成,将无法重用。因此,例如,多次单击新StackBlitz中的按钮将不起作用。使用哪种方法(第一个可以反复单击或完成的方法)取决于您需要的结果。
创建两个intervel$
可观察对象,一个用于中间UI更新,另一个用于最后一个。将它们合并在一起,仅在订阅中进行UI更新。 StackBlitz使用此选项
代码:
const start$ = fromEvent(document.getElementById('start'), 'click')
const intervel1$ = interval(100)
.pipe(
take(x)
);
const intervel2$ = interval(100)
.pipe(
take(x+1),
last(),
mapTo('Final')
);
const startInterval$ = start$
.pipe(
switchMapTo(merge(intervel1$, intervel2$))
);
startInterval$
.subscribe(
val => console.log('Update UI: ', val)
);
import { switchMapTo, tap, take, last, share, mapTo } from 'rxjs/operators';
import { fromEvent, interval, merge } from 'rxjs';
const x = 5;
const start$ = fromEvent(document.getElementById('start'), 'click');
const intervel$ = interval(100);
const intervel1$ = intervel$
.pipe(
take(x)
);
const intervel2$ = intervel1$
.pipe(
last(),
mapTo('Final')
);
const startInterval$ = start$
.pipe(
switchMapTo(merge(intervel1$, intervel2$))
);
startInterval$
.subscribe(
val => console.log('Update UI: ', val)
);
原始问题的关键问题是“以不同的方式使用相同的可观测对象”,即在进度和最终过程中。因此merge
是解决这类问题的一种相当不错的逻辑模式