Como retornar o valor da função que possui assinatura Observable dentro?


97

Não sei como extrair valor de Observable para ser retornado pela função em que Observable está presente. Preciso apenas de um valor para ser devolvido, nada mais.

Versão atual que funciona

function getValueFromObservable() {
    this.store.subscribe(
        (data:any) => {
            console.log(data)
        }
    )
}
getValueFromObservable()

Preciso que isso funcione, funcione para retornar o valor e, em seguida:

function getValueFromObservable() {
    this.store.subscribe(
        (data:any) => {
            return data
        }
    )
}
console.log(getValueFromObservable())

O que eu estou fazendo errado aqui?


2
Você deve retornar um Observável / Promessa e passar os dados por meio dele quando seu observável for resolvido
galvan

2
Você pode colocar algum código simples para isso?
Teddy

6
O que você está tentando alcançar é um antipadrão: você está tentando "sincronizar" uma tarefa assíncrona. Não é assim que os observáveis ​​devem funcionar. Resumindo, na maioria dos casos, uma função com um observável como entrada também deve retornar um observável - ou não retornar nada. E quando você precisar fazer algo com a saída, assine-a. Neste caso, se você deseja console.logar os dados, basta fazê-lo dentrosubscribe
Can Nguyen

1
Eu entendo tudo que você disse. Estou apenas usando o log do console como demonstração, vou usar esses dados ainda mais, é por isso que preciso deles para o log do console fora do observável. O objetivo é ter a função que, quando você pode inscrever o observável, obter dados, cancelar a inscrição e retornar dados nessa função, para que eu possa usar esses dados posteriormente. Eu sei que é anti-padrão, mas preciso que funcione. Qualquer ajuda é apreciada. Atualmente, minha solução funciona, mas não estou muito confiante sobre ela.
Teddy

4
Atenção por FAVOR! O código da seção 'SOLUÇÃO' está absolutamente incorreto. Não use isso! Funcionará apenas se a seção this.store.subscribe ((data: any) => {output = data}) .unsubscribe () for finalizada até o retorno. Caso contrário, ele retornará indefinido.
Rodion Golovushkin

Respostas:


56

EDITAR: código atualizado para refletir as mudanças feitas na forma como os tubos funcionam nas versões mais recentes do RXJS. Todos os operadores (veja meu exemplo) agora estão agrupados no operador pipe ().

Sei que essa questão foi há um bom tempo e com certeza você tem uma solução adequada agora, mas para quem está procurando por isso, sugiro resolvê-la com uma promessa de manter o padrão assíncrono.

Uma versão mais detalhada seria criar uma nova promessa:

function getValueFromObservable() {
    return new Promise(resolve=>{
        this.store.pipe(
           take(1) //useful if you need the data once and don't want to manually cancel the subscription again
         )
         .subscribe(
            (data:any) => {
                console.log(data);
                resolve(data);
         })
    })
}

Na ponta receptora, você terá então "esperar" que a promessa seja resolvida com algo assim:

getValueFromObservable()
   .then((data:any)=>{
   //... continue with anything depending on "data" after the Promise has resolved
})

Uma solução mais simples seria usar RxJS '.toPromise () em vez disso:

function getValueFromObservable() {
    return this.store.pipe(take(1))
       .toPromise()   
}

O lado receptor permanece o mesmo que acima, é claro.


Qual é o tipo de retorno da sua getValueFromObservablefunção?
hardywang

Deve ser uma promessa com qualquer tipo de dado de armazenamento. por exemplo, Promise <StoreType>
jparg

4
você ainda está retornando uma promessa que precisa ser resolvida e não retornando um valor diretamente.
Roj de

1
A propriedade 'take' não existe no tipo 'Observable <>'
Memmo

1
@Memmo tente .pipe (take (1)) em vez
Thibault

20

Esta não é exatamente a ideia correta de usar Observable

No componente, você deve declarar o membro da classe que conterá um objeto (algo que você vai usar em seu componente)

export class MyComponent {
  name: string = "";
}

Então, um Serviceestará retornando para você um Observable:

getValueFromObservable():Observable<string> {
    return this.store.map(res => res.json());
}

Component deve se preparar para conseguir recuperar um valor dele:

OnInit(){
  this.yourServiceName.getValueFromObservable()
    .subscribe(res => this.name = res.name)
}

Você deve atribuir um valor de a Observablea uma variável:

E seu modelo consumirá variáveis name:

<div> {{ name }} </div>

Outra forma de uso Observableé através do asyncpipe http://briantroncone.com/?p=623

Observação : se não for o que você está perguntando, atualize sua pergunta com mais detalhes


Bem, não exatamente. O problema é que os dados são capturados dentro do observável e posso apenas registrá-los no console. Quero retornar esse valor e console.log ou qualquer outro arquivo diferente, chamando a função na qual ele reside.
Teddy

Andrei mostrou como namedisponibilizar fora do callback atribuindo-o à namevariável do componente . Não é possível retornar namesincronizadamente no seu caso.
Matt

@Matt: Não posso usá-lo Oninitdessa forma, e se eu precisar retornar explicitamente, Meu código de chamada se parece com isto this.actions$.ofType(SearchActions.SEARCH_MULTIPLE_NEW_QUERY).map(toPayload).fnWithMultipleAsyncReturns()
ishandutta2007

@ ishandutta2007 Olá. É melhor você criar uma nova pergunta no SO sobre seu problema.
Matt,

@Matt: criado, caso você queira dar uma olhada ( stackoverflow.com/questions/43381922/… )
ishandutta2007

7

Se você quiser fazer uma pré-assinatura do mesmo Observable que será retornado, basta usar

.Faz():

function getValueFromObservable() {
    return this.store.do(
        (data:any) => {
            console.log("Line 1: " +data);
        }
    );
}

getValueFromObservable().subscribe(
        (data:any) => {
            console.log("Line 2: " +data)
        }
    );

3
Você também pode usar outros operadores como o .map(data => data)que faz a mesma coisa e, em seguida,
inscrevê-

Eu concordo com ashok_khuman. Aqui está o guia angular.io/guide/pipes
Armando Perea

Essa poderia ser uma boa resposta, mas na verdade você não explicou nada a respeito, torna-se uma resposta ruim. O que significa "pré-inscrição"? E deve resolver a questão do abridor de tópicos?
Florian Leitgeb

note que no RxJS 6 doagora é chamado tape você deve usá-lo em um pipe. Observe também que tapusa vários parâmetros para manipuladores diferentes, como next, completee error.
Simon_Weaver

7

O problema é que os dados são capturados dentro do observável e posso apenas registrá-los no console. Quero retornar esse valor e console.log ou qualquer outro arquivo diferente, chamando a função na qual ele reside.

Parece que você está procurando por um getter de "valor atual" dentro de um observável, quando ele emite e após uma emissão.

Subjecte Observablenão tem tal coisa. Quando um valor é emitido, ele é passado para seus assinantes e Observableé feito com ele.

Você pode usar o BehaviorSubjectqual armazena o último valor emitido e o emite imediatamente para novos assinantes.

Também possui um getValue()método para obter o valor atual;

Leitura adicional:

RxJS BehaviorSubject

Como obter o valor atual do Assunto RxJS ou Observável?


2

Os valores observáveis ​​podem ser recuperados de qualquer local. A sequência de origem é primeiro enviada a um observador especial capaz de emitir em outro lugar. Isso é obtido com a classe Assunto das extensões reativas (RxJS).

var subject = new Rx.AsyncSubject();  // store-last-value method

Armazene o valor no observador .

subject.next(value); // store value
subject.complete(); // publish only when sequence is completed

Para recuperar o valor de outro lugar, inscreva-se no observador assim:

subject.subscribe({
  next: (response) => {
      //do stuff. The property name "response" references the value
  }
});

Os assuntos são observáveis ​​e observadores. Existem outros tipos de assunto , como BehaviourSubject e ReplaySubject para outros cenários de uso.

Não se esqueça de importar RxJS.

var Rx = require('rxjs');

1

Embora as respostas anteriores possam funcionar de certa forma, acho que usar BehaviorSubject é a maneira correta se você quiser continuar usando observáveis.

Exemplo:

    this.store.subscribe(
        (data:any) => {
            myService.myBehaviorSubject.next(data)
        }
    )

No Serviço:

let myBehaviorSubject = new BehaviorSubjet(value);

Em component.ts:

this.myService.myBehaviorSubject.subscribe(data => this.myData = data)

Eu espero que isso ajude!


0

Por exemplo, este é o meu modelo html:

<select class="custom-select d-block w-100" id="genre" name="genre"
                  [(ngModel)]="film.genre"
                  #genreInput="ngModel"
                  required>
            <option value="">Choose...</option>
            <option *ngFor="let genre of genres;" [value]="genre.value">{{genre.name}}</option>
          </select>

Este é o campo vinculado ao modelo do meu componente:

  // Genres of films like action or drama that will populate dropdown list.
  genres: Genre[];

Eu busco gêneros de filmes do servidor dinamicamente. Para me comunicar com o servidor que crieiFilmService

Este é o método que comunica o servidor:

 fetchGenres(): Observable<Genre[]> {
    return this.client.get(WebUtils.RESOURCE_HOST_API + 'film' + '/genre') as Observable<Genre[]>;
  }

Por que esse método Observable<Genre[]>não retorna algo como Genre[]?

JavaScript é asynce não espera que um método retorne valor após um processo caro. Com caro quero dizer um processo que demora a retornar valor. Como buscar dados do servidor. Então você tem que retornar a referência do Observable e assiná-la.

Por exemplo, em meu componente:

ngOnInit() {
    this.filmService.fetchGenres().subscribe(
      val => this.genres = val
    );
  }

0
function getValueFromObservable() {
    this.store.subscribe(
        (data:any) => {
            return data
        }
    )
}
console.log(getValueFromObservable())

No caso acima, console.log é executado antes que a promessa seja resolvida, então nenhum valor é exibido, altere para o seguinte

function getValueFromObservable() {
    return this.store
}

getValueFromObservable()
 .subscribe((data: any) => {
    // do something here with data
    console.log(data);
});

outra solução é quando você precisa de dados dentro de getValueFromObservable para retornar o uso observável de operador e assinar a função.

 function getValueFromObservable() {
        return this.store.subscribe((data: any) => {
            // do something with data here
            console.log(data);
            //return again observable.
            return of(data);
       })
    }

    getValueFromObservable()
     .subscribe((data: any) => {
        // do something here with data
        console.log(data);
    });

0

No mundo do javascript de thread único, assíncrono, orientado a promessas e tendências reativas, async/awaité o melhor amigo do programador de estilo imperativo:

(async()=>{

    const store = of("someValue");
    function getValueFromObservable () {
        return store.toPromise();
    }
    console.log(await getValueFromObservable())

})();

E caso storeseja uma sequência de múltiplos valores:

  const aiFrom = require('ix/asynciterable').from;
  (async function() {

     const store = from(["someValue","someOtherValue"]);
     function getValuesFromObservable () {
        return aiFrom(store);
     }
     for await (let num of getValuesFromObservable()) {
       console.log(num);
     }
  })();

0

A maneira decente seria retornar o observável de uma função e assiná-lo sempre que necessário, porque os observáveis ​​são preguiçosos, eles começarão a emitir valores apenas quando forem assinados.

Aqui eu tenho mais uma solução orientada a eventos interessante, com a qual inicialmente usei para brincar. O exemplo a seguir faz isso usando o módulo " events " de nodejs. Você pode usá-lo com outras estruturas onde existe um módulo semelhante ( Observação : a sintaxe e o estilo podem mudar dependendo do módulo usado).

var from =require("rxjs").from;
var map = require("rxjs/operators").map;
var EventEmitter = require("events");

function process(event) {
    from([1,2,3]).pipe(
        map(val => `The number is:: ${val}`)
    ).subscribe((data) => {
       event.emit("Event1", data); //emit value received in subscribe to the "Event1" listener
    });
}

function main() {
   class Emitter extends EventEmitter{};
    var event = new Emitter(); //creating an event
    event.on("Event1", (data)=>{ //listening to the event of name "Event1" and callback to log returned result
        console.log(data); //here log, print, play with the data you receive
    });
    process(event); //pass the event to the function which returns observable.
}

main(); //invoke main function

É apenas um exemplo para mostrar uma ideia onde podemos passar dados de diferentes lugares pelo método de emissão e escuta. Isso também é conhecido como código orientado a eventos.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.