Conheça adiar

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 - é tapusado para executar a função, o sinalizador é runnecessá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 deferaceita uma função que deve retornar ObservableInput. O código interno deferserá 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 deferpara 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);

// The same concept as using a function
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.


// This already executing regardless the numbers of handlers
const promise = new Promise(resolve => {
  resolve();
});

// Deferring the creation of the promise until someone subscribes
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.


All Articles