¿Cómo devolver el valor de la función que tiene una suscripción observable dentro?

98

No sé cómo extraer valor de Observable para que lo devuelva la función en la que Observable está presente. Necesito solo un valor de él para ser devuelto, nada más.

Versión actual que funciona

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

Necesito que esto funcione, que funcione para devolver el valor, y luego:

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

¿Qué estoy haciendo mal aquí?

Osito de peluche
fuente
2
Debe devolver un Observable / Promise y pasar los datos a través de él cuando se resuelva su observable
galvan
2
¿Puedes poner un código simple para esto?
Teddy
6
Lo que está tratando de lograr es un anti-patrón: está tratando de "sincronizar" una tarea asíncrona. Esa no es la forma en que se supone que funcionan los observables. En resumen, en la mayoría de los casos, una función que tiene un observable como entrada también debería devolver un observable, o no devolver nada. Y cuando necesite hacer algo con la salida, suscríbase a ella. En este caso, si desea console.log los datos, simplemente hágalo adentrosubscribe
Can Nguyen
1
Entiendo todo lo que dijiste. Solo estoy usando el registro de la consola como demostración, usaré esos datos más, por eso lo necesito para el registro de la consola fuera del observable. El punto es tener la función que cuando pueda suscribir lo observable, obtener datos, cancelar la suscripción y devolver datos en esa función para que pueda usar esos datos más. Sé que es anti-patrón, pero necesito que funcione. Se agradece cualquier ayuda. Actualmente, mi solución funciona, pero no confío demasiado en ella.
Teddy
4
¡Atención por favor! El código de la sección 'SOLUCIÓN' es absolutamente incorrecto. ¡No lo uses! Solo funcionará si la sección this.store.subscribe ((data: any) => {output = data}) .unsubscribe () estará terminada hasta que regrese. De lo contrario, volverá sin definir.
Rodion Golovushkin

Respuestas:

58

EDITAR: código actualizado para reflejar los cambios realizados en la forma en que funcionan las tuberías en versiones más recientes de RXJS. Todos los operadores (tome en mi ejemplo) ahora están envueltos en el operador pipe ().

Me doy cuenta de que esta pregunta fue hace bastante tiempo y seguramente ya tiene una solución adecuada, pero para cualquiera que busque esto, sugeriría que la resuelva con una promesa de mantener el patrón asíncrono.

Una versión más detallada sería la creación de una nueva promesa:

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);
         })
    })
}

En el extremo receptor, tendrá que "esperar" a que la promesa se resuelva con algo como esto:

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

Una solución más delgada sería usar RxJS '.toPromise () en su lugar:

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

El lado receptor permanece igual que el anterior, por supuesto.

jparg
fuente
¿Cuál es el tipo de retorno de su getValueFromObservablefunción?
Hardywang
Debería ser una promesa con cualquier tipo de datos de la tienda. por ejemplo, Promise <StoreType>
jparg
4
todavía está devolviendo una promesa que debe resolverse y no devolviendo un valor directamente.
Roj
1
La propiedad 'toma' no existe en el tipo 'Observable <>'
Memmo
1
@Memmo prueba .pipe (toma (1)) en su lugar
Thibault
20

Esta no es exactamente la idea correcta de usar Observable

En el componente, debe declarar el miembro de la clase que contendrá un objeto (algo que usará en su componente)

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

Entonces Servicete devolverá un Observable:

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

Component debe prepararse para poder recuperar un valor de él:

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

Tienes que asignar un valor de una Observablea una variable:

Y su plantilla consumirá la variable name:

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

Otra forma de uso Observablees mediante asynctubería http://briantroncone.com/?p=623

Nota : Si no es lo que está preguntando, actualice su pregunta con más detalles.

Andrei Zhytkevich
fuente
Bueno, no del todo. El problema es que los datos se capturan dentro del observable y puedo simplemente registrarlos en la consola. Quiero devolver ese valor y console.log o lo que sea desde un archivo diferente llamando a la función en la que reside.
Teddy
Andrei señaló cómo hacer namedisponible fuera de la devolución de llamada asignándolo a la namevariable del componente . No es posible regresar namesincrónicamente en su caso.
Matt
@Matt: No puedo usarlo Oninitasí, ¿qué pasa si necesito regresar explícitamente? Mi código de llamada se ve así this.actions$.ofType(SearchActions.SEARCH_MULTIPLE_NEW_QUERY).map(toPayload).fnWithMultipleAsyncReturns()
ishandutta2007
@ ishandutta2007 Hola. Será mejor que cree una nueva pregunta sobre SO con respecto a su problema.
Matt
@Matt: creado, en caso de que quieras echar un vistazo ( stackoverflow.com/questions/43381922/… )
ishandutta2007
7

Si desea suscribirse previamente al mismo Observable que se devolverá, solo use

.hacer():

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

getValueFromObservable().subscribe(
        (data:any) => {
            console.log("Line 2: " +data)
        }
    );
Dudi
fuente
3
También puede usar otros operadores como .map(data => data)que hacen lo mismo y luego suscribirlo donde espere el resultado
ashok_khuman
Estoy de acuerdo con ashok_khuman. Aquí está la guía angular.io/guide/pipes
Armando Perea
Esta podría ser una buena respuesta, pero de hecho, el hecho de que no hayas explicado nada al respecto la convierte en una mala respuesta. ¿Qué significa "pre-suscripción"? ¿Y debería resolver la pregunta del abridor de hilos?
Florian Leitgeb
tenga en cuenta que en RxJS 6 doahora se llama tapy debe usarlo en una tubería. También tenga en cuenta que taptoma múltiples parámetros para diferentes controladores como next, completey error.
Simon_Weaver
7

El problema es que los datos se capturan dentro del observable y puedo simplemente registrarlos en la consola. Quiero devolver ese valor y console.log o lo que sea desde un archivo diferente llamando a la función en la que reside.

Parece que está buscando un captador de "valor actual" dentro de un observable, cuando emite y después de una emisión.

Subjecty Observableno tiene tal cosa. Cuando se emite un valor, se pasa a sus suscriptores y Observablese hace con él.

Puede utilizar BehaviorSubjectque almacena el último valor emitido y lo emite inmediatamente a los nuevos suscriptores.

También tiene un getValue()método para obtener el valor actual;

Otras lecturas:

Comportamiento de RxJS Sujeto

¿Cómo obtener el valor actual del sujeto u observable RxJS?

A. Alencar
fuente
2

Los valores observables se pueden recuperar de cualquier ubicación. La secuencia de origen se envía primero a un observador especial que puede emitir en otro lugar. Esto se logra con la clase Subject de Reactive Extensions (RxJS).

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

Almacene valor en el observador .

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

Para recuperar el valor de otro lugar, suscríbase al observador así:

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

Los sujetos son tanto observables como observadores. Hay otros tipos de sujetos como BehaviourSubject y ReplaySubject para otros escenarios de uso.

No olvide importar RxJS.

var Rx = require('rxjs');
Estudio Pageii
fuente
1

Si bien las respuestas anteriores pueden funcionar de alguna manera, creo que usar BehaviorSubject es la forma correcta si desea continuar usando observables.

Ejemplo:

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

En el servicio:

let myBehaviorSubject = new BehaviorSubjet(value);

En component.ts:

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

¡Espero que esto ayude!

Shlomo Koppel
fuente
0

Por ejemplo, esta es mi plantilla 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 es el campo que se enlazó con la plantilla de mi componente:

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

Busco géneros de películas del servidor de forma dinámica. Para comunicarse con el servidor que he creadoFilmService

Este es el método que comunica el servidor:

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

¿Por qué este método Observable<Genre[]>no devuelve algo como Genre[]?

JavaScript es asyncy no espera a que un método devuelva valor después de un proceso costoso. Con caro me refiero a un proceso que tarda un tiempo en devolver valor. Como recuperar datos del servidor. Por lo tanto, debe devolver la referencia de Observable y suscribirse.

Por ejemplo, en mi componente:

ngOnInit() {
    this.filmService.fetchGenres().subscribe(
      val => this.genres = val
    );
  }
Muhammed Ozdogan
fuente
0
function getValueFromObservable() {
    this.store.subscribe(
        (data:any) => {
            return data
        }
    )
}
console.log(getValueFromObservable())

En el caso anterior, console.log se ejecuta antes de que se resuelva la promesa, por lo que no se muestra ningún valor, cámbialo a lo siguiente

function getValueFromObservable() {
    return this.store
}

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

otra solución es cuando necesita datos dentro de getValueFromObservable para devolver el operador observable y suscribirse a la función.

 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);
    });
iaq
fuente
0

En el mundo de JavaScript de tendencia reactiva, asíncrono, orientado a promesas y de un solo subproceso, se async/awaitencuentra el mejor amigo del programador de estilo imperativo:

(async()=>{

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

})();

Y en caso de que storesea ​​una secuencia de múltiples 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);
     }
  })();
Marinos An
fuente
¿Por qué debería usar la asincronía en el caso de una matriz de datos?
Janos Vinceller
@JanosVinceller Este es solo un ejemplo, para enfocarse en otras partes del código. Puede reemplazarlo from(["someValue","someOtherValue"])con un observable de su elección que emita más de un valor. Supongo que podría ser más adecuado haberlo usado interval(1000).
Marinos An
0

La forma decente sería devolver el observable de una función y suscribirse a él donde sea necesario, porque los observables son perezosos, comenzarán a emitir valores solo cuando estén suscritos.

Aquí tengo una solución impulsada por eventos más interesante, con la que inicialmente solía jugar. El siguiente ejemplo hace esto usando el módulo " eventos " de nodejs. Puede usarlo con otros marcos donde exista un módulo similar ( Nota : la sintaxis y el estilo pueden cambiar según el módulo utilizado).

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

Es solo un ejemplo para mostrar una idea en la que podemos pasar datos de diferentes lugares mediante el método de emisión y escucha. Esto también se conoce como código controlado por eventos.

varad11
fuente