Bagaimana cara mengembalikan nilai dari fungsi yang memiliki langganan Observable di dalamnya?


98

Saya tidak tahu cara mengekstrak nilai dari Observable untuk dikembalikan oleh fungsi di mana Observable ada. Saya hanya perlu nilai darinya untuk dikembalikan, tidak ada yang lain.

Versi saat ini yang berfungsi

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

Saya membutuhkan ini untuk bekerja, berfungsi untuk mengembalikan nilai, dan kemudian:

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

Apa yang saya lakukan salah di sini?


2
Anda harus mengembalikan Observable / Promise dan meneruskan datanya saat observable Anda diselesaikan
galvan

2
Dapatkah Anda memberikan kode sederhana untuk ini?
Teddy

6
Apa yang Anda coba capai adalah anti-pola: Anda mencoba untuk "menyinkronkan" tugas asinkron. Seharusnya itu bukan cara kerja observable. Singkatnya, dalam banyak kasus, fungsi yang memiliki observable sebagai input juga harus mengembalikan observable - atau tidak mengembalikan apa pun. Dan ketika Anda perlu melakukan sesuatu dengan hasilnya, berlanggananlah. Dalam hal ini jika Anda ingin console.log data, lakukan saja di dalamsubscribe
Bisa Nguyen

1
Saya mengerti semua yang Anda katakan. Saya hanya menggunakan log konsol sebagai demo, saya akan menggunakan data itu lebih lanjut, itulah mengapa saya membutuhkannya untuk log konsol di luar yang dapat diamati. Intinya adalah memiliki fungsi yang ketika Anda dapat berlangganan observasi, mendapatkan data, berhenti berlangganan dan mengembalikan data dalam fungsi itu sehingga saya dapat menggunakan data itu lebih lanjut. Saya tahu ini anti-pola, tapi saya membutuhkannya untuk bekerja. Setiap bantuan dihargai. Saat ini solusi saya berfungsi, tetapi saya tidak terlalu yakin tentang itu.
Teddy

4
Dimohon perhatiannya! Kode dari bagian 'SOLUSI' sama sekali tidak benar. Jangan gunakan itu! Ini hanya akan berfungsi jika bagian this.store.subscribe ((data: any) => {output = data}) .unsubscribe () akan diselesaikan hingga dikembalikan. Jika tidak, itu akan kembali tidak ditentukan.
Rodion Golovushkin

Jawaban:


58

EDIT: kode yang diperbarui untuk mencerminkan perubahan yang dilakukan pada cara kerja pipa di versi RXJS yang lebih baru. Semua operator (ambil contoh saya) sekarang dibungkus ke dalam operator pipe ().

Saya menyadari bahwa Pertanyaan ini sudah cukup lama dan Anda pasti memiliki solusi yang tepat sekarang, tetapi bagi siapa pun yang mencari ini, saya sarankan untuk menyelesaikannya dengan Janji untuk mempertahankan pola asinkron.

Versi yang lebih panjang akan membuat Janji baru:

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

Di pihak penerima, Anda kemudian harus "menunggu" janji diselesaikan dengan sesuatu seperti ini:

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

Solusi yang lebih ramping akan menggunakan RxJS '.toPromise () sebagai gantinya:

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

Sisi penerima tetap sama seperti di atas tentunya.


Apa tipe kembalian dari getValueFromObservablefungsi Anda ?
hardywang

Harus menjadi janji dengan jenis data penyimpanan apa pun. mis. Janji <StoreType>
jparg

4
Anda masih mengembalikan janji yang perlu diselesaikan dan tidak mengembalikan nilai secara langsung.
Roj

1
Properti 'take' tidak ada pada tipe 'Observable <>'
Memmo

1
@Memmo coba .pipe (ambil (1)) sebagai gantinya
Thibault

20

Ini bukanlah ide penggunaan yang benar Observable

Di dalam komponen Anda harus mendeklarasikan anggota kelas yang akan menampung sebuah objek (sesuatu yang akan Anda gunakan dalam komponen Anda)

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

Kemudian a Serviceakan mengembalikan Anda sebuah Observable:

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

Component harus mempersiapkan diri untuk dapat mengambil nilai darinya:

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

Anda harus menetapkan nilai dari sebuah Observableke variabel:

Dan template Anda akan menggunakan variabel name:

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

Cara lain untuk menggunakan Observableadalah melalui asyncpipa http://briantroncone.com/?p=623

Catatan : Jika bukan itu yang Anda tanyakan, harap perbarui pertanyaan Anda dengan lebih detail


Kurang tepat. Masalahnya adalah bahwa data ditangkap di dalam yang dapat diamati dan saya hanya bisa membuat log konsol. Saya ingin mengembalikan nilai itu dan console.log atau apa pun dari file yang berbeda dengan memanggil fungsi tempatnya berada.
Teddy

Andrei menunjukkan bagaimana membuat yang nametersedia di luar callback dengan menugaskannya ke namevariabel komponen . Tidak mungkin untuk kembali secara namesinkron dalam kasus Anda.
Matt

@Matt: Saya tidak bisa menggunakannya Oninitseperti itu, Bagaimana jika saya perlu mengembalikan secara eksplisit, kode panggilan saya terlihat seperti ini this.actions$.ofType(SearchActions.SEARCH_MULTIPLE_NEW_QUERY).map(toPayload).fnWithMultipleAsyncReturns()
ishandutta2007

@ ishandutta2007 Halo. Anda lebih baik membuat pertanyaan baru tentang SO tentang masalah Anda.
Matt

@Matt: dibuat, jika Anda ingin melihatnya ( stackoverflow.com/questions/43381922/… )
ishandutta2007

7

Jika Anda ingin berlangganan Observable yang sama yang akan dikembalikan, gunakan saja

.melakukan():

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

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

3
Anda juga dapat menggunakan operator lain seperti .map(data => data)yang melakukan hal yang sama dan kemudian berlangganan di mana pun Anda mengharapkan hasilnya
ashok_khuman

Saya setuju dengan ashok_khuman. Berikut adalah panduan angular.io/guide/pipes
Armando Perea

Ini bisa jadi jawaban yang bagus, tapi nyatanya kamu tidak menjelaskan apapun tentangnya, membuatnya menjadi jawaban yang buruk. Apa artinya "pra-berlangganan"? Dan haruskah itu menyelesaikan pertanyaan dari pembuka utas?
Florian Leitgeb

perhatikan bahwa di RxJS 6 dosekarang dipanggil tapdan Anda harus menggunakannya dalam pipa. Perhatikan juga bahwa tapmengambil beberapa parameter untuk penangan yang berbeda seperti next, completedan error.
Simon_Weaver

7

Masalahnya adalah bahwa data ditangkap di dalam yang dapat diamati dan saya hanya bisa membuat log konsol. Saya ingin mengembalikan nilai itu dan console.log atau apa pun dari file yang berbeda dengan memanggil fungsi tempatnya berada.

Sepertinya Anda sedang mencari getter "nilai saat ini" di dalam sebuah observable, saat ia memancarkan dan setelah emisi.

Subjectdan Observabletidak memiliki hal seperti itu. Ketika sebuah nilai dipancarkan, itu diteruskan ke pelanggannya dan Observableselesai dengannya.

Anda dapat menggunakan BehaviorSubjectyang menyimpan nilai emisi terakhir dan segera memancarkannya ke pelanggan baru.

Ia juga memiliki getValue()metode untuk mendapatkan nilai saat ini;

Bacaan lebih lanjut:

RxJS BehaviorSubject

Bagaimana cara mendapatkan nilai RxJS Subject atau Observable?


2

Nilai yang dapat diamati dapat diambil dari lokasi manapun. Urutan sumber pertama - tama didorong ke pengamat khusus yang dapat memancarkan di tempat lain. Ini dicapai dengan kelas Subjek dari Ekstensi Reaktif (RxJS).

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

Simpan nilai ke pengamat .

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

Untuk mengambil nilai dari tempat lain, berlangganan pengamat seperti ini:

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

Subjek adalah Observable dan Observers. Ada jenis Subjek lain seperti BehaviourSubject dan ReplaySubject untuk skenario penggunaan lainnya.

Jangan lupa untuk mengimpor RxJS.

var Rx = require('rxjs');

1

Meskipun jawaban sebelumnya mungkin berhasil, saya pikir menggunakan BehaviorSubject adalah cara yang benar jika Anda ingin terus menggunakan observable.

Contoh:

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

Dalam Layanan:

let myBehaviorSubject = new BehaviorSubjet(value);

Di component.ts:

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

Saya harap ini membantu!


0

Misalnya ini adalah template html saya:

<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>

Ini adalah bidang yang diikat dengan templat dari Komponen saya:

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

Saya mengambil genre film dari server secara dinamis. Untuk berkomunikasi dengan server yang telah saya buatFilmService

Ini adalah metode yang mengkomunikasikan server:

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

Mengapa metode ini mengembalikan Observable<Genre[]>bukan sesuatu seperti Genre[]?

JavaScript adalah asyncdan tidak menunggu metode untuk mengembalikan nilai setelah proses yang mahal. Dengan mahal yang saya maksud adalah proses yang membutuhkan waktu untuk mengembalikan nilai. Seperti mengambil data dari server. Jadi, Anda harus mengembalikan referensi Observable dan berlangganan.

Misalnya di Komponen saya:

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

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

Dalam kasus di atas, console.log berjalan sebelum promise diselesaikan sehingga tidak ada nilai yang ditampilkan, ubah menjadi mengikuti

function getValueFromObservable() {
    return this.store
}

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

solusi lainnya adalah ketika Anda membutuhkan data di dalam getValueFromObservable untuk mengembalikan penggunaan operator yang dapat diamati dan berlangganan fungsi.

 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

Dalam dunia javascript single-threaded, asynchronous, promise-oriented, reactive-trending async/awaitadalah sahabat programmer imperatif-style:

(async()=>{

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

})();

Dan dalam kasus storeini adalah urutan beberapa nilai:

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

Mengapa saya menggunakan asynchronousness dalam kasus data array?
Janos Vinceller

@JanosVinceller Ini hanyalah sebuah contoh, untuk berfokus pada bagian lain dari kode. Anda dapat mengganti from(["someValue","someOtherValue"])dengan observasi pilihan Anda yang memancarkan lebih dari satu nilai. Saya kira, mungkin lebih cocok digunakan interval(1000).
Marinos An

0

Cara yang layak adalah mengembalikan observable dari suatu fungsi dan berlangganan ke sana kapan pun diperlukan, karena observable itu malas, mereka akan mulai memancarkan nilai hanya ketika mereka berlangganan.

Di sini saya memiliki satu lagi solusi yang didorong oleh peristiwa yang menarik, yang awalnya saya gunakan untuk bermain-main. Contoh berikut melakukan ini dengan menggunakan modul " peristiwa " dari nodejs. Anda dapat menggunakannya dengan kerangka kerja lain di mana modul serupa ada ( Catatan : Sintaks dan gaya mungkin berubah tergantung pada modul yang digunakan).

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

Ini hanyalah sebuah contoh untuk menunjukkan sebuah ide dimana kita dapat mengirimkan data dari tempat yang berbeda dengan metode pemancaran dan pendengaran. Ini juga dikenal sebagai kode yang digerakkan oleh peristiwa.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.