Crie uma assinatura única

182

Preciso criar uma assinatura para uma Observableque seja imediatamente descartada quando for chamada pela primeira vez.

Existe algo como:

observable.subscribeOnce(func);

No meu caso de uso, estou criando uma assinatura em um manipulador de rota expressa e a assinatura está sendo chamada várias vezes por solicitação.

Berkeley Martinez
fonte

Respostas:

319

Não está 100% certo do que você precisa, mas se você deseja apenas observar o primeiro valor, use um first()ou take(1):

observable.first().subscribe(func);

nota: .take(1)e .first()cancelam a inscrição automaticamente quando sua condição é atendida

Atualização do RxJS 5.5+

Do comentário de Coderer .

import { first } from 'rxjs/operators'

observable
  .pipe(first())
  .subscribe(func);

Aqui está o porquê

Brandon
fonte
33
A assinatura é limpa automaticamente depois?
Berkeley Martinez
50
Sim. Faça e primeiro cancele a inscrição quando sua condição for atendida.
Brandon
20
Por que a documentação não diz que descarta automaticamente a assinatura?
jzig
17
É uma regra geral do RxJS que as assinaturas são descartadas quando um fluxo observável termina. Isso significa que qualquer operador que "encurte" um fluxo (ou o transforme em um tipo diferente de fluxo) cancelará a inscrição no fluxo de origem quando sua operação for concluída. Não tenho certeza de onde ou se isso está indicado na documentação.
Brandon
37
Se alguém está chegando a isso em 2018, você realmente quer observable.pipe(first()).subscribe(func), de onde firstvem rxjs/operators.
precisa saber é o seguinte
32

O RxJS tem algumas das melhores documentações que já encontrei. Seguir o link abaixo o levará a uma tabela extremamente útil para mapear casos de uso para operadores. Por exemplo, no âmbito do "eu quero tomar o primeiro valor" caso de uso são três operadores: first, firstOrDefault, e sample.

Observe que, se uma sequência observável for concluída sem notificações, o firstoperador notificará os assinantes com um erro enquanto o firstOrDefaultoperador fornecerá um valor padrão aos assinantes.

pesquisa de caso de uso do operador

DetweilerRyan
fonte
3

Se você quiser chamar um Observable apenas uma vez, isso significa que você não esperará um fluxo dele. Portanto, usar em toPromise()vez de subscribe()seria suficiente no seu caso, pois toPromise()não precisa de cancelamento de inscrição.

M Fuat NUROĞLU
fonte
muito interessante, isso também significa que podemos exatamente awaito promiseque o torna único
Louie Almeda 21/03
@LouieAlmeda, você poderia nos dar um exemplo único?
Ado Ren
Olá @AdoRen, expus a técnica em uma resposta aqui para você, espero que ajude.
Louie Almeda
2

Para complementar a resposta de @ Brandon , usar first()ou algo semelhante também é essencial para atualizar uma com BehaviorSubjectbase em sua Observable. Por exemplo (não testado):

var subject = new BehaviorSubject({1:'apple',2:'banana'});
var observable = subject.asObservable();

observable
  .pipe(
    first(), // <-- Ensures no stack overflow
    flatMap(function(obj) {
      obj[3] = 'pear';
      return of(obj);
    })
  )
  .subscribe(function(obj) {
    subject.next(obj);
  });
Andy
fonte
2

Versão Limpa e Conveniente

Expandindo a incrível resposta do M Fuat NUROĞLU em converter o observável em uma promessa, aqui está a versão muito conveniente.

const value = await observable.toPromise();

console.log(value)

A vantagem disso é que podemos usar esse valor como uma variável normal sem introduzir outro bloco aninhado!

Isso é especialmente útil quando você precisa obter vários valores de vários observáveis. Arrumado e limpo.

const content = await contentObservable.toPromise();
const isAuthenticated = await isAuthenticatedObservable.toPromise();

if(isAuthenticated){
   service.foo(content)
}

Obviamente, você terá que fazer a sua função de contenção asyncse quiser seguir esta rota. Você também pode apenas .thenprometer se não deseja que a função que contém seja assíncrona

Não tenho certeza se existem vantagens e desvantagens com essa abordagem. Fique à vontade para nos informar nos comentários, para que possamos estar cientes.

PS Se você gostou desta resposta, não se esqueça de votar também na resposta de M Fuat NUROĞLU :)

Louie Almeda
fonte
0

Eu tive uma pergunta semelhante.

Abaixo foi chamado ainda mais tarde a partir de diferentes alteradores de estado. Como eu não queria.

function foo() {
    // this was called many times which was not needed
    observable.subscribe(func);
    changeObservableState("new value");
}

Eu decidi tentar unsubscribe()depois de assinar como abaixo.

function foo() {
    // this was called ONE TIME
    observable.subscribe(func).unsubscribe();
    changeObservableState("new value");
}

subscribe(func).unsubscribe();é como subscribeOnce(func).

Espero que tenha ajudado você também.

MrHIDEn
fonte