Pausar um intervalo rxjs
Eu tenho um cronômetro simples, com o uso de rxjs
O problema é: não consigo pausar um stream do meu intervalo e, em seguida, continuar stackbiz
Respostas
Já vi perguntas sobre cronômetros surgirem com tanta frequência que imaginei que seria interessante criar um cronômetro observável personalizado. A maneira RxJS seria implementar isso alternando dentro e fora de temporizadores / intervalos.
Outra maneira interessante de implementar isso é usar setTimeout. setTimeout deve realmente exigir um pouco menos de memória, pois não estamos nos apoiando no aparato observável para cumprir nossos objetivos
Como isso vai funcionar? Nosso observável personalizado cria um fluxo que produz o número no cronômetro e é controlado por um fluxo separado (aqui chamado control$). Portanto, quando control$emite "START", o stopWatch inicia, quando emite "STOP", o cronômetro para e quando emite "RESET" o cronômetro zera o contador. Quando control$erros ou são concluídos, o cronômetro comete erros ou é concluído.
Implementado com switchMap e Timer
function createStopwatch(control$: Observable<string>, interval = 1000): Observable<number>{
return defer(() => {
let toggle: boolean = false;
let count: number = 0;
const endTicker$ = new Subject(); const ticker = () => { return timer(0, interval).pipe( takeUntil(endTicker$),
map(x => count++)
)
}
return control$.pipe( tap({ next: _ => {/*Do nothing*/}, complete: () => { endTicker$.next();
endTicker$.complete(); }, error: err => { endTicker$.next();
endTicker$.complete();
}
}),
filter(control =>
control === "START" ||
control === "STOP" ||
control === "RESET"
),
switchMap(control => {
if(control === "START" && !toggle){
toggle = true;
return ticker();
}else if(control === "STOP" && toggle){
toggle = false;
return EMPTY;
}else if(control === "RESET"){
count = 0;
if(toggle){
return ticker();
}
}
return EMPTY;
})
);
});
}
Implementado com setTimeout
function createStopwatch(control: Observable<string>, interval = 1000): Observable<number> {
return new Observable(observer => {
let count: number = 0;
let tickerId: number = null;
const clearTicker = () => {
if(tickerId != null){
clearTimeout(tickerId);
tickerId = null;
}
}
const setTicker = () => {
const recursiveTicker = () => {
tickerId = setTimeout(() => {
observer.next(count++);
recursiveTicker();
}, interval);
}
clearTicker();
observer.next(count++);
recursiveTicker();
}
control.subscribe({
next: input => {
if(input === "START" && tickerId == null){
setTicker();
}else if(input === "STOP"){
clearTicker();
}else if(input === "RESET"){
count = 0;
if(tickerId != null){
setTicker();
}
}
},
complete: () => {
clearTicker();
observer.complete();
},
error: err => {
clearTicker();
observer.error(err);
}
});
return {unsubscribe: () => clearTicker()};
});
}
StopWatch in Use
Aqui está um exemplo deste observável sendo usado. Eu gerencio o fluxo de controle por meio de um assunto, mas poderia facilmente ser eventos DOM mesclados / mapeados ou algo assim.
const control$ = new Subject<string>();
createStopwatch(control$, 250).subscribe(console.log); // We send a new action to our control stream every 1 second const actions = ["START", "STOP", "START", "RESET", "START"] zip(from(actions), interval(1000)).pipe( map((x,y) => x), finalize(() => { // After 5 seconds, unsubscribe via the control // If our control finishes in any way ( // completes, errors, or is unsubscribed), our // sopwatch reacts by doing the same. control$.complete();
})
).subscribe(x => control$.next(x));
StopWatch in Use # 2
Isso controla o cronômetro com em setTimeoutvez de interval.
const control$ = new Subject<string>();
createStopwatch(control$, 250).subscribe(console.log); // We send a new action to our control stream every 1 second const actions = ["START", "STOP", "START", "RESET", "START"] actions.forEach((val, index) => { setTimeout(() => { control$.next(val);
},
index * 1000);
})
// Unsubscribe via the control
setTimeout(() => {
control$.complete();
}, actions.length * 1000);
Dê uma olhada na minha solução neste stackblitz
O componente deve se inscrever no StopWatch-Service. Não gosto da ideia de atribuir um valor a um serviço e depois o serviço MUDAR o valor implicitamente. Portanto, trabalho com uma maneira explícita de obter o stopWatch atualizado (como um observável).
Eu trabalho com "cronômetro" em vez de "intervalo", porque "intervalo" emitiria o primeiro valor (a 0) após um segundo, portanto, meu stopWath teria um atraso,
Existe um pequeno truque. Existe uma variável privada "timer $", um BehaviorSubject. E assim que o contador é iniciado, eu inicio um cronômetro e assino-o. Nessa assinatura, cada emissão do cronômetro emitirá um novo valor para o BehaviorSubject.
Agora também armazeno a assinatura.
Se eu quiser parar tudo, basta cancelar a assinatura do "cronômetro". Como resultado, o relógio pára. Mas, por estar meio desacoplado do comportamentoSubjeto timer$, ainda será armazenado o último valor.
Ou, para dizer de outra forma:
o BehaviorSubject vive para sempre, o consumidor nunca cancela a assinatura (apenas se ele mesmo fizer isso). E eu meio que "anexei" e "desanexei" a função do cronômetro a ela, sempre que a contagem deveria começar ou parar.
A propósito, é um bom hábito sempre certificar-se de que seu componente cancelará a inscrição de todos os observáveis em execução, quando o componente for destruído. Se isso não for feito, essas assinaturas ainda ativas podem resultar em desempenho ou problemas ainda piores em um aplicativo.
espero que ajude um pouco
Atenciosamente