Jak zwrócić wartość z funkcji, która ma wewnątrz subskrypcję Observable?

98

Nie wiem, jak wyodrębnić wartość z Observable, aby została zwrócona przez funkcję, w której obecny jest Observable. Potrzebuję tylko wartości z tego, aby została zwrócona, nic więcej.

Aktualna wersja, która działa

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

Potrzebuję tego do działania, funkcji do zwracania wartości, a następnie:

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

Co ja tu robię źle?

Miś
źródło
2
Powinieneś zwrócić Observable / Promise i przekazać dane za jego pośrednictwem, gdy twój obserwowalny zostanie rozwiązany
galvan
2
Czy możesz podać prosty kod?
Teddy,
6
To, co próbujesz osiągnąć, to anty-wzorzec: próbujesz „zsynchronizować” zadanie asynchroniczne. Nie w ten sposób powinny działać obserwable. Krótko mówiąc, w większości przypadków funkcja, której dane wejściowe są obserwowalne, powinna również zwracać obserwowalne - lub nic nie zwracać. A kiedy musisz coś zrobić z wyjściem, zasubskrybuj go. W takim przypadku, jeśli chcesz konsolować.logować dane, po prostu zrób to w środkusubscribe
Can Nguyen
1
Rozumiem wszystko, co powiedziałeś. Używam tylko dziennika konsoli jako demonstracji, będę używać tych danych dalej, dlatego potrzebuję go do konsoli logowania poza obserwowalnym. Chodzi o to, aby mieć funkcję, która kiedy można subskrybować obserwowalne, pobierać dane, anulować subskrypcję i zwracać dane w tej funkcji, abym mógł dalej korzystać z tych danych. Wiem, że to anty-wzór, ale potrzebuję, żeby zadziałał. Każda pomoc jest mile widziana. Obecne moje rozwiązanie działa, ale nie jestem co do tego zbyt przekonany.
Teddy,
4
Proszę o uwagę! Kod z sekcji „ROZWIĄZANIE” jest całkowicie niepoprawny. Nie używaj tego! Będzie działać tylko wtedy, gdy sekcja this.store.subscribe ((data: any) => {output = data}) .unsubscribe () zostanie zakończona aż do powrotu. W przeciwnym razie zwróci wartość undefined.
Rodion Golovushkin

Odpowiedzi:

58

EDYCJA: zaktualizowany kod w celu odzwierciedlenia zmian wprowadzonych w sposobie działania rur w nowszych wersjach RXJS. Wszystkie operatory (w moim przykładzie) są teraz opakowane w operator pipe ().

Zdaję sobie sprawę, że to pytanie było dawno temu i na pewno masz już właściwe rozwiązanie, ale dla każdego, kto go szuka, sugerowałbym rozwiązanie go z Obietnicą zachowania wzorca asynchronicznego.

Bardziej szczegółowa wersja stworzyłaby nową obietnicę:

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

Po stronie odbiorczej będziesz musiał „czekać” na rozwiązanie obietnicy za pomocą czegoś takiego:

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

Węższym rozwiązaniem byłoby użycie .toPromise () RxJS zamiast:

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

Oczywiście strona odbiorcza pozostaje taka sama jak powyżej.

jparg
źródło
Jaki jest typ zwracanej getValueFromObservablefunkcji?
hardywang
Powinna być obietnicą bez względu na typ danych sklepu. np. Promise <StoreType>
jparg
4
nadal zwracasz obietnicę, która musi zostać rozwiązana i nie zwracasz bezpośrednio wartości.
Roj
1
Właściwość „take” nie istnieje dla typu „Observable <>”
Memmo
1
@Memmo spróbuj zamiast tego .pipe (weź (1))
Thibault
20

To nie jest do końca poprawny pomysł użycia Observable

W komponencie musisz zadeklarować składową klasy, która będzie zawierała obiekt (coś, czego zamierzasz użyć w swoim komponencie)

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

Wtedy Servicebędzie wrócił ci Observable:

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

Component powinien przygotować się na odzyskanie z niego wartości:

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

Musisz przypisać wartość z an Observabledo zmiennej:

Twój szablon będzie zużywał zmienną name:

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

Innym sposobem użycia Observablejest użycie asyncpotoku http://briantroncone.com/?p=623

Uwaga : jeśli nie o to pytasz, zaktualizuj swoje pytanie, podając więcej szczegółów

Andrei Zhytkevich
źródło
Cóż, niezupełnie. Problem polega na tym, że dane są przechwytywane w obserwowalnym i mogę je po prostu zalogować w konsoli. Chcę zwrócić tę wartość i console.log lub cokolwiek z innego pliku, wywołując funkcję, w której się znajduje.
Teddy,
Andrei wskazał, jak udostępnić to, co znajduje się namepoza wywołaniem zwrotnym, przypisując je do namezmiennej składnika . W nametwoim przypadku nie ma możliwości powrotu synchronicznego.
Matt
@Matt: Nie mogę tego użyć w Oninitten sposób, a co jeśli muszę zwrócić jawnie, mój kod wywoławczy wygląda tak this.actions$.ofType(SearchActions.SEARCH_MULTIPLE_NEW_QUERY).map(toPayload).fnWithMultipleAsyncReturns()
ishandutta2007
@ ishandutta2007 Cześć. Lepiej utwórz nowe pytanie na SO dotyczące Twojego problemu.
Matt
@Matt: utworzony, na wypadek gdybyś chciał się przyjrzeć ( stackoverflow.com/questions/43381922/… )
ishandutta2007
7

Jeśli chcesz wstępnie zasubskrybować to samo Observable, które zostanie zwrócone, po prostu użyj

.zrobić():

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

getValueFromObservable().subscribe(
        (data:any) => {
            console.log("Line 2: " +data)
        }
    );
Dudi
źródło
3
Możesz także użyć innych operatorów, takich jak .map(data => data)który robi to samo, a następnie zasubskrybować go wszędzie tam, gdzie spodziewasz się wyniku
ashok_khuman
Zgadzam się z ashok_khuman. Oto przewodnik angular.io/guide/pipes
Armando Perea
To może być dobra odpowiedź, ale tak naprawdę to, że nic na ten temat nie wyjaśniłeś, prowadzi do złej odpowiedzi. Co oznacza „pre-subskrybowanie”? I czy powinno rozwiązać problem z otwieraczem nici?
Florian Leitgeb,
Zauważ, że w RxJS 6 dojest teraz wywoływany tapi musisz go użyć w potoku. Należy również zauważyć, że tapprzyjmuje wiele parametrów dla różnych programów obsługi, takich jak next, completei error.
Simon_Weaver
7

Problem polega na tym, że dane są przechwytywane w obserwowalnym i mogę je po prostu zalogować w konsoli. Chcę zwrócić tę wartość i console.log lub cokolwiek z innego pliku, wywołując funkcję, w której się znajduje.

Wygląda na to, że szukasz elementu pobierającego „wartość bieżącą” wewnątrz obiektu obserwowalnego, kiedy emituje i po emisji.

Subjecti Observablenie ma czegoś takiego. Kiedy wartość jest emitowana, jest przekazywana jej subskrybentom i Observablekończy się z nią.

Możesz skorzystać z tego, BehaviorSubjectktóry przechowuje ostatnią wyemitowaną wartość i natychmiast emituje ją nowym abonentom.

Posiada również getValue()metodę uzyskiwania bieżącej wartości;

Dalsze czytanie:

RxJS BehaviorSubject

Jak uzyskać aktualną wartość przedmiotu RxJS lub obserwowalnego?

A. Alencar
źródło
2

Obserwowalne wartości można pobrać z dowolnej lokalizacji. Sekwencja źródłowa jest najpierw wypychana na specjalnego obserwatora, który może emitować gdzie indziej. Osiąga się to za pomocą klasy Subject z Reactive Extensions (RxJS).

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

Przechowuj wartość dla obserwatora .

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

Aby pobrać wartość z innego miejsca, zasubskrybuj obserwatora w następujący sposób:

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

Podmioty są zarówno obserwatorami, jak i obserwatorami. Istnieją inne typy podmiotów, takie jak BehaviourSubject i ReplaySubject dla innych scenariuszy użycia.

Nie zapomnij zaimportować RxJS.

var Rx = require('rxjs');
Pageii Studio
źródło
1

Chociaż poprzednie odpowiedzi mogą działać w pewien sposób, myślę, że używanie BehaviorSubject jest właściwym sposobem, jeśli chcesz nadal używać obserwabli.

Przykład:

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

W serwisie:

let myBehaviorSubject = new BehaviorSubjet(value);

W component.ts:

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

Mam nadzieję, że to pomoże!

Shlomo Koppel
źródło
0

Na przykład to jest mój szablon 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>

To jest pole, które zostało powiązane z szablonem z mojego komponentu:

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

Dynamicznie pobieram gatunki filmów z serwera. W celu komunikacji z utworzonym przeze mnie serweremFilmService

To jest metoda komunikacji z serwerem:

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

Dlaczego ta metoda Observable<Genre[]>nie zwraca czegoś takiego Genre[]?

JavaScript jest asynci nie czeka, aż metoda zwróci wartość po kosztownym procesie. Przez kosztowny mam na myśli proces, który wymaga czasu, aby zwrócić wartość. Podobnie jak pobieranie danych z serwera. Więc trzeba zwrócić odniesienie obserwowalnych i zapisz go.

Na przykład w moim komponencie:

ngOnInit() {
    this.filmService.fetchGenres().subscribe(
      val => this.genres = val
    );
  }
Muhammed Ozdogan
źródło
0
function getValueFromObservable() {
    this.store.subscribe(
        (data:any) => {
            return data
        }
    )
}
console.log(getValueFromObservable())

W powyższym przypadku console.log zostanie uruchomiona, zanim obietnica zostanie rozwiązana, więc żadna wartość nie jest wyświetlana, zmień ją na następującą

function getValueFromObservable() {
    return this.store
}

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

innym rozwiązaniem jest, gdy potrzebujesz danych wewnątrz getValueFromObservable, aby zwrócić obserwowalne użycie operatora i zasubskrybować funkcję.

 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
źródło
0

W jednowątkowym, asynchronicznym, zorientowanym na obietnice, reaktywnym świecie javascript async/awaitjest najlepszym przyjacielem programisty w stylu imperatywnym:

(async()=>{

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

})();

A w przypadku, gdy storejest to sekwencja wielu wartości:

  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
źródło
Dlaczego miałbym używać asynchroniczności w przypadku tablicy danych?
Janos Vinceller
@JanosVinceller To jest tylko przykład do skupienia się na innych częściach kodu. Możesz zastąpić from(["someValue","someOtherValue"])wybraną obserwowalną, która emituje więcej niż jedną wartość. Myślę, że bardziej odpowiednie byłoby użycie interval(1000).
Marinos An
0

Przyzwoitym sposobem byłoby zwrócenie obserwowalnego z funkcji i zasubskrybowanie go tam, gdzie jest to wymagane, ponieważ obserwowalne są leniwe, zaczną emitować wartości tylko wtedy, gdy zostaną zasubskrybowane.

Tutaj mam jeszcze jedno interesujące rozwiązanie oparte na wydarzeniach, z którym początkowo się bawiłem. Poniższy przykład robi to za pomocą modułu „ events ” programu nodejs. Możesz go używać z innymi frameworkami, w których istnieje podobny moduł ( Uwaga : składnia i styl mogą się zmieniać w zależności od używanego modułu).

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

To tylko przykład pokazujący pomysł, w którym możemy przekazywać dane z różnych miejsc metodą emisji i nasłuchu. Jest to również znane jako kod sterowany zdarzeniami.

varad11
źródło