Вы были очень близки.Используйте last () внутри intervel $, чтобы отправить только последний подписчик ниже.Рабочая 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()
, то вы можете просто заставить start $ закончить, взяв только первую эмиссию изатем завершается либо take(1)
, либо first()
. Вот новый 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')
);
Недостатком этого подхода (или любого подхода, дополняющего наблюдаемое) является то, что после его завершения он не будет использоваться повторно.Так, например, многократное нажатие на кнопку в новом StackBlitz не сработает.Какой подход использовать (первый, на который можно нажимать снова и снова, или тот, который завершается) зависит от результатов, которые вам нужны.
Еще один вариант
Создание двух intervel$
наблюдаемых,один для промежуточных обновлений пользовательского интерфейса и один для последнего.Объедините их вместе и выполняйте только обновление пользовательского интерфейса в подписке. 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)
);
Более идиоматический способ, та же логика, что и у предыдущего (By Guichi)
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
- вполне приличная логическая модель для решения этой проблемы