Em um artigo anterior , vimos como criar nossas próprias instruções RxJS. Agora, quero falar sobre um operador de criação pouco conhecido - defer
- e explicar como você pode usá-lo em algumas situações
Suponha que você precise criar um operador que aceite uma função e a execute apenas uma vez, na primeira vez que receber um valor. Nós o implementamos com o nome tapOnce
function tapOnce<T>(fn: Function): OperatorFunction<T, T> {
return function(source: Observable<any>) {
let run = false;
return source.pipe(
tap(() => {
if (!run) {
fn();
run = true;
}
})
);
};
}
O código é claro - é tap
usado para executar a função, o sinalizador é run
necessário para fazer isso apenas uma vez. Agora usamos o operador.
const source = interval(5000).pipe(
tapOnce(() => console.log('+')
));
source.subscribe(console.log);
Tudo funciona, o sinal de adição é exibido no console apenas na primeira emissão. Agora adicione os assinantes.
const source = interval(5000).pipe(
tapOnce(() => console.log('+')
));
source.subscribe(console.log);
source.subscribe(console.log);
Se você olhar para o console - há apenas um plus. O problema é que ambos os assinantes usam o mesmo ambiente lexical e se referem à mesma variável run
. Precisamos de uma maneira de adiar a criação de um thread até que alguém se inscreva.
Vai ajudar defer
import { defer } from 'rxjs';
function tapOnce<T>(fn: Function): OperatorFunction<T, T> {
return function(source: Observable<any>) {
return defer(() => {
let run = false;
return source.pipe(
tap(() => {
if (!run) {
fn();
run = true;
}
})
);
});
};
}
O operador defer
aceita uma função que deve retornar ObservableInput
. O código interno defer
será executado somente na assinatura, e não durante a criação. Usando essa abordagem e graças ao fechamento do js, cada assinante usa seu próprio ambiente lexical.
Vamos criar nossa implementação simples defer
para uma melhor compreensão.
function defer(observableFactory: () => ObservableInput<any>) {
return new Observable(subscriber => {
const source = observableFactory();
return source.subscribe(subscriber);
});
}
defer
retorna um novo fluxo, que é criado no momento da assinatura pela função de fábrica e será usado como fonte.
Aqui estão mais exemplos em que será útil defer
. Digamos que tenhamos uma expressão que precise ser contada quando alguém se inscrever. por exemplo
const randNum = of(Math.random());
randNum.subscribe(console.log);
randNum.subscribe(console.log);
Neste exemplo, cada assinante receberá o mesmo valor aleatório. Você pode corrigi-lo para que a expressão seja contada ao se inscrever, não ao anunciar.
const randNum = defer(() => of(Math.random()));
randNum2.subscribe(console.log);
randNum2.subscribe(console.log);
const randNum = () => of(Math.random());
randNum2().subscribe(console.log);
randNum2().subscribe(console.log);
Outro exemplo é quando você precisa atrasar a execução de uma promessa.
const promise = new Promise(resolve => {
resolve();
});
const promiseDefered = defer(() => {
return new Promise(resolve => {
resolve();
});
});
promiseDefered.subscribe(console.log);
As promessas são executadas imediatamente, independentemente do número de ouvintes. Você pode fazer uma promessa parecer um fluxo (ou seja, preguiçoso) usando defer
.