จะคืนค่าจากฟังก์ชันที่มีการสมัครสมาชิกแบบสังเกตได้อย่างไร?


98

ฉันไม่รู้วิธีดึงค่าจาก Observable เพื่อส่งคืนโดยฟังก์ชันที่ Observable มีอยู่ ฉันต้องการเพียงมูลค่าจากมันเพื่อส่งคืนไม่มีอะไรอื่น

เวอร์ชันปัจจุบันที่ใช้งานได้

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

ฉันต้องการให้สิ่งนี้ใช้งานได้ฟังก์ชั่นคืนค่าแล้ว:

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

ฉันทำอะไรผิดที่นี่?


2
คุณควรส่งคืน Observable / Promise และส่งผ่านข้อมูลเมื่อการสังเกตของคุณได้รับการแก้ไข
galvan

2
คุณสามารถใส่รหัสง่ายๆสำหรับสิ่งนี้ได้หรือไม่?
Teddy

6
สิ่งที่คุณพยายามจะบรรลุคือการต่อต้านรูปแบบ: คุณกำลังพยายาม "ซิงโครไนซ์" งาน async นั่นไม่ใช่วิธีที่ผู้สังเกตการณ์ควรจะได้ผล กล่าวโดยย่อในกรณีส่วนใหญ่ฟังก์ชันที่มีค่าที่สังเกตได้เนื่องจากอินพุตควรส่งคืนค่าที่สังเกตได้ด้วยหรือไม่ส่งคืนค่าอะไรเลย และเมื่อคุณต้องการทำอะไรบางอย่างกับผลลัพธ์ให้สมัครสมาชิก ในกรณีนี้ถ้าคุณต้องการ console.log ข้อมูลก็ทำได้ภายในsubscribe
สามารถเหงียน

1
ฉันเข้าใจทั้งหมดที่คุณพูด ฉันแค่ใช้คอนโซลบันทึกเป็นตัวอย่างฉันจะใช้ข้อมูลนั้นต่อไปนั่นเป็นเหตุผลว่าทำไมฉันถึงต้องใช้มันเพื่อคอนโซลบันทึกภายนอกที่สังเกตได้ ประเด็นคือต้องมีฟังก์ชั่นที่เมื่อคุณสามารถสมัครสมาชิกที่สังเกตได้รับข้อมูลยกเลิกการสมัครและส่งคืนข้อมูลในฟังก์ชันนั้นเพื่อให้ฉันสามารถใช้ข้อมูลนั้นต่อไปได้ ฉันรู้ว่ามันต่อต้านรูปแบบ แต่ฉันต้องการให้มันใช้งานได้ ขอความช่วยเหลือใด ๆ ปัจจุบันโซลูชันของฉันใช้งานได้ แต่ฉันไม่มั่นใจมากเกินไป
Teddy

4
โปรดทราบ! รหัสจากส่วน 'โซลูชัน' ไม่ถูกต้องอย่างยิ่ง อย่าใช้มัน! จะใช้งานได้ก็ต่อเมื่อส่วน this.store.subscribe ((data: any) => {output = data}) .unsubscribe () จะเสร็จสิ้นจนกว่าจะกลับมา มิฉะนั้นจะกลับมาไม่ได้กำหนด
Rodion Golovushkin

คำตอบ:


58

แก้ไข: โค้ดที่อัปเดตเพื่อแสดงการเปลี่ยนแปลงที่เกิดขึ้นกับวิธีการทำงานของไพพ์ใน RXJS เวอร์ชันล่าสุด ขณะนี้ตัวดำเนินการทั้งหมด (ในตัวอย่างของฉัน) ถูกรวมเข้ากับตัวดำเนินการ pipe ()

ฉันรู้ว่าคำถามนี้ค่อนข้างนานมาแล้วและตอนนี้คุณมีวิธีแก้ปัญหาที่เหมาะสม แต่สำหรับใครก็ตามที่กำลังมองหาสิ่งนี้ฉันขอแนะนำให้แก้ปัญหาด้วยสัญญาว่าจะรักษารูปแบบ async

เวอร์ชันที่ละเอียดมากขึ้นจะสร้างสัญญาใหม่:

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

เมื่อสิ้นสุดการรับคุณจะต้อง "รอ" สำหรับคำสัญญาที่จะแก้ไขในสิ่งนี้:

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

โซลูชันที่บางกว่าจะใช้ RxJS '.toPromise () แทน:

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

ด้านรับยังคงเหมือนเดิมแน่นอน


ประเภทผลตอบแทนของgetValueFromObservableฟังก์ชันของคุณคืออะไร?
hardywang

ควรเป็นสัญญากับข้อมูลประเภทใดก็ตามที่จัดเก็บ เช่น Promise <StoreType>
jparg

4
คุณยังคงคืนสัญญาที่ต้องได้รับการแก้ไขและไม่คืนค่าโดยตรง
โรจน์

1
คุณสมบัติ 'take' ไม่มีอยู่ในประเภท 'Observable <>'
Memmo

1
@Memmo ลอง. pipe (ใช้ (1)) แทน
Thibault

20

นี่ไม่ใช่แนวคิดที่ถูกต้องในการใช้ Observable

ในส่วนประกอบคุณต้องประกาศสมาชิกชั้นเรียนซึ่งจะถือวัตถุ (สิ่งที่คุณกำลังจะใช้ในส่วนประกอบของคุณ)

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

จากนั้นServiceจะส่งคืนคุณObservable:

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

Component ควรเตรียมตัวให้พร้อมเพื่อให้สามารถดึงค่าจากมันได้:

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

คุณต้องกำหนดค่าจากObservableตัวแปรเป็น:

และเทมเพลตของคุณจะใช้ตัวแปรname:

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

อีกวิธีหนึ่งในการใช้งานObservableคือการใช้asyncท่อhttp://briantroncone.com/?p=623

หมายเหตุ : หากไม่ใช่สิ่งที่คุณกำลังถามโปรดอัปเดตคำถามของคุณพร้อมรายละเอียดเพิ่มเติม


ไม่ค่อยดี ปัญหาคือข้อมูลถูกจับภายในสิ่งที่สังเกตได้และฉันสามารถบันทึกมันได้ ฉันต้องการคืนค่านั้นและ console.log หรืออะไรก็ตามจากไฟล์อื่นโดยเรียกใช้ฟังก์ชันที่มันอยู่
Teddy

Andrei ชี้ให้เห็นถึงวิธีทำให้nameพร้อมใช้งานนอกการโทรกลับโดยกำหนดให้กับnameตัวแปรของคอมโพเนนต์ ไม่สามารถส่งคืนแบบnameซิงโครนัสได้ในกรณีของคุณ
Matt

@Matt: ฉันไม่สามารถใช้มันในOninitแบบนั้นได้ถ้าฉันต้องการคืนอย่างชัดเจนรหัสการโทรของฉันจะมีลักษณะเช่นนี้ this.actions$.ofType(SearchActions.SEARCH_MULTIPLE_NEW_QUERY).map(toPayload).fnWithMultipleAsyncReturns()
ishandutta2007

@ ishandutta2007 เฮ้มี คุณควรสร้างคำถามใหม่ใน SO เกี่ยวกับปัญหาของคุณ
แมตต์

@ แมท: สร้างแล้ว
เผื่อ

7

หากคุณต้องการสมัครสมาชิกล่วงหน้ากับ Observable เดียวกันซึ่งจะถูกส่งคืนเพียงใช้

.ทำ():

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

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

3
คุณยังสามารถใช้โอเปอเรเตอร์อื่น ๆ.map(data => data)ที่ทำในสิ่งเดียวกันแล้วสมัครสมาชิกได้ทุกที่ที่คุณคาดหวังผลลัพธ์
ashok_khuman

ฉันเห็นด้วยกับ ashok_khuman นี่คือคำแนะนำangular.io/guide/pipes
Armando Perea

นี่อาจเป็นคำตอบที่ดี แต่ในความเป็นจริงที่คุณไม่ได้อธิบายอะไรเกี่ยวกับเรื่องนี้ทำให้เป็นคำตอบที่ไม่ดี "สมัครสมาชิกล่วงหน้า" หมายความว่าอย่างไร และควรแก้ปัญหาจากการเปิดเธรดหรือไม่?
Florian Leitgeb

โปรดทราบว่าใน RxJS 6 doเรียกว่าตอนนี้tapและคุณต้องใช้ในท่อ นอกจากนี้ทราบว่าtapจะใช้เวลาหลายพารามิเตอร์สำหรับการขนย้ายวัสดุที่แตกต่างกันเช่นnext, และcomplete error
Simon_Weaver

7

ปัญหาคือข้อมูลถูกจับภายในสิ่งที่สังเกตได้และฉันสามารถบันทึกมันได้ ฉันต้องการคืนค่านั้นและ console.log หรืออะไรก็ตามจากไฟล์อื่นโดยเรียกใช้ฟังก์ชันที่มันอยู่

ดูเหมือนว่าคุณกำลังมองหา "มูลค่าปัจจุบัน" ที่อยู่ภายในค่าที่สังเกตได้เมื่อปล่อยออกมาและหลังจากการปล่อย

SubjectและObservableไม่มีสิ่งนั้น เมื่อปล่อยค่าออกมาค่าจะถูกส่งต่อไปยังสมาชิกและObservableดำเนินการเสร็จสิ้น

คุณอาจใช้ BehaviorSubjectที่เก็บค่าที่ปล่อยออกมาล่าสุดและส่งให้กับสมาชิกใหม่ทันที

นอกจากนี้ยังมีgetValue()วิธีการรับค่าปัจจุบัน

อ่านเพิ่มเติม:

พฤติกรรม RxJS

จะรับค่าปัจจุบันของ RxJS Subject หรือ Observable ได้อย่างไร?


2

สามารถเรียกดูค่าที่สังเกตได้จากสถานที่ใด ๆ ลำดับแหล่งที่มาจะถูกส่งไปยังผู้สังเกตการณ์พิเศษที่สามารถปล่อยออกมาที่อื่นได้ก่อน สิ่งนี้ทำได้ด้วยคลาส Subjectจาก Reactive Extensions (RxJS)

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

เก็บค่าเข้าสู่สังเกตการณ์

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

หากต้องการดึงค่าจากที่อื่นให้สมัครสมาชิกผู้สังเกตการณ์ดังนี้:

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

วัตถุเป็นทั้งผู้สังเกตและผู้สังเกตการณ์ มีประเภทหัวเรื่องอื่น ๆเช่น BehaviourSubject และ ReplaySubject สำหรับสถานการณ์การใช้งานอื่น ๆ

อย่าลืมนำเข้า RxJS

var Rx = require('rxjs');

1

แม้ว่าคำตอบก่อนหน้านี้อาจใช้ได้ผล แต่ฉันคิดว่าการใช้ BehaviorSubject เป็นวิธีที่ถูกต้องหากคุณต้องการใช้สิ่งที่สังเกตได้ต่อไป

ตัวอย่าง:

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

ในบริการ:

let myBehaviorSubject = new BehaviorSubjet(value);

ใน component.ts:

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

ฉันหวังว่านี่จะช่วยได้!


0

ตัวอย่างเช่นนี่คือเทมเพลต 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>

นี่คือฟิลด์ที่ผูกกับเทมเพลตจากส่วนประกอบของฉัน:

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

ฉันดึงข้อมูลประเภทของภาพยนตร์จากเซิร์ฟเวอร์แบบไดนามิก เพื่อสื่อสารกับเซิร์ฟเวอร์ที่ฉันสร้างขึ้นFilmService

นี่คือวิธีที่ใช้สื่อสารกับเซิร์ฟเวอร์:

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

ทำไมวิธีนี้ถึงObservable<Genre[]>ไม่ส่งคืนสิ่งที่ต้องการGenre[]?

JavaScript คือasyncและไม่รอให้เมธอดส่งคืนค่าหลังจากกระบวนการที่มีราคาแพง ด้วยราคาแพงฉันหมายถึงกระบวนการที่ต้องใช้เวลาในการคืนค่า เช่นเดียวกับการดึงข้อมูลจากเซิร์ฟเวอร์ ดังนั้นคุณต้องส่งคืนการอ้างอิงของ Observable และสมัครสมาชิก

ตัวอย่างเช่นในส่วนประกอบของฉัน:

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

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

ในกรณีข้างต้น console.log ทำงานก่อนที่คำสัญญาจะได้รับการแก้ไขดังนั้นจึงไม่มีการแสดงค่าให้เปลี่ยนเป็นดังต่อไปนี้

function getValueFromObservable() {
    return this.store
}

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

วิธีแก้ปัญหาอื่นคือเมื่อคุณต้องการข้อมูลภายใน getValueFromObservable เพื่อส่งคืนค่าที่สังเกตได้โดยใช้ตัวดำเนินการและสมัครรับฟังก์ชั่น

 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

ในโลกของจาวาสคริปต์แบบอะซิงโครนัสแบบเธรดเดียวแบบอะซิงโครนัสที่เน้นการตอบสนองและมีแนวโน้มที่จะตอบสนองasync/awaitคือเพื่อนที่ดีที่สุดของโปรแกรมเมอร์สไตล์ที่จำเป็น:

(async()=>{

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

})();

และในกรณีที่storeเป็นลำดับของหลายค่า:

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

เหตุใดฉันจึงใช้ความอะซิงโครนัสในกรณีของอาร์เรย์ข้อมูล
Janos Vinceller

@JanosVinceller นี่เป็นเพียงตัวอย่างสำหรับเน้นส่วนอื่น ๆ ของโค้ด คุณสามารถแทนที่from(["someValue","someOtherValue"])ด้วยค่าที่สังเกตได้ที่คุณเลือกซึ่งมีค่ามากกว่าหนึ่งค่า interval(1000)ผมคิดว่ามันอาจจะมีความเหมาะสมที่จะได้ใช้
Marinos

0

วิธีที่ดีคือส่งคืนค่าที่สังเกตได้จากฟังก์ชันและสมัครสมาชิกทุกที่ที่ต้องการเนื่องจากผู้สังเกตการณ์ขี้เกียจพวกเขาจะเริ่มปล่อยค่าเมื่อมีการสมัครสมาชิกเท่านั้น

ที่นี่ฉันมีอีกหนึ่งโซลูชันที่ขับเคลื่อนด้วยเหตุการณ์ที่น่าสนใจซึ่งในตอนแรกฉันเคยเล่นด้วย ตัวอย่างต่อไปนี้ทำได้โดยใช้โมดูล " events " ของ nodejs คุณสามารถใช้กับเฟรมเวิร์กอื่น ๆ ที่มีโมดูลที่คล้ายกันอยู่ ( หมายเหตุ : ไวยากรณ์และรูปแบบอาจเปลี่ยนแปลงขึ้นอยู่กับโมดูลที่ใช้)

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

เป็นเพียงตัวอย่างในการแสดงแนวคิดที่เราสามารถส่งผ่านข้อมูลจากที่ต่างๆโดยวิธีการเปล่งเสียงและการฟัง ซึ่งเรียกอีกอย่างว่ารหัสที่ขับเคลื่อนด้วยเหตุการณ์

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.