Як повернути значення з функції, яка має спостережну підписку всередині?


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
Чи можете ви поставити простий код для цього?
Тедді,

6
Що ви намагаєтесь досягти, це анти-шаблон: Ви намагаєтесь "синхронізувати" асинхронне завдання. Це не спосіб спостережуваних повинен працювати. Коротше кажучи, у більшості випадків функція, яка має спостережуване в якості вхідних даних, також повинна повертати спостережуване - або нічого не повертати. І коли вам потрібно щось зробити з результатом, підпишіться на це. У цьому випадку, якщо ви хочете console.log дані, просто зробіть це всерединіsubscribe
Can Nguyen

1
Я розумію все, що ви сказали. Я просто використовую консольний журнал як демонстрацію, я буду використовувати ці дані далі, ось чому він мені потрібен для консольного журналу поза спостережуваним. Сенс у тому, щоб мати функцію, яка, коли ви можете підписати спостережуване, отримати дані, скасувати підписку та повернути дані в цій функції, щоб я міг використовувати ці дані далі. Я знаю, що це анти-шаблон, але мені це потрібно для роботи. Будь-яка допомога вдячна. Наразі моє рішення працює, але я не надто впевнений у цьому.
Тедді

4
Увага будь-ласка! Код із розділу "РІШЕННЯ" абсолютно неправильний. Не використовуйте його! Це буде працювати лише в тому випадку, якщо розділ this.store.subscribe ((data: any) => {output = data}) .unsubscribe () буде закінчено до повернення. В іншому випадку він повернеться невизначеним.
Родіон Головкушкін

Відповіді:


58

РЕДАКТУВАТИ: оновлений код для відображення змін, внесених у спосіб роботи трубопроводів у більш пізніх версіях RXJS. Усі оператори (візьмемо мій приклад) тепер обернуті в оператор pipe ().

Я усвідомлюю, що це Питання було досить давно, і Ви напевно вже маєте належне рішення, але для тих, хто шукає це, я б запропонував вирішити його за допомогою Promise, щоб зберегти асинхронний шаблон.

Більш багатослівною версією було б створення нового Promise:

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 (take (1)) замість цього
Тібо

20

Це не зовсім правильна ідея використання Observable

У компоненті ви повинні оголосити члена класу, який буде містити об'єкт (те, що ви збираєтеся використовувати у своєму компоненті)

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

Тоді a Serviceповерне вам Observable:

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

Component повинен підготуватися до можливості отримати з нього значення:

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

Вам потрібно призначити значення від an Observableдо змінної:

І ваш шаблон буде споживати змінну name:

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

Інший спосіб використання Observable- це asyncтруба http://briantroncone.com/?p=623

Примітка . Якщо це не те, про що ви запитуєте, оновіть своє запитання, докладно


Ну не зовсім. Проблема в тому, що дані фіксуються всередині спостережуваного, і я можу просто консолірувати їх. Я хочу повернути це значення та console.log або що-небудь з іншого файлу, викликавши функцію, в якій він знаходиться.
Тедді

Андрій зазначив, як зробити nameдоступним за межами зворотного виклику, призначивши його nameзмінній компонента . Повернути nameсинхронно у вашому випадку неможливо .
Matt

@Matt: Я не можу використовувати його Oninitтак, Що, якщо мені потрібно явно повернутися, Мій телефонний код виглядає так this.actions$.ofType(SearchActions.SEARCH_MULTIPLE_NEW_QUERY).map(toPayload).fnWithMultipleAsyncReturns()
ishandutta2007

@ ishandutta2007 Привіт. Вам краще створити нове запитання щодо SO стосовно своєї проблеми.
Метт,

@ Matt: створений, в разі , якщо ви хочете поглянути ( stackoverflow.com/questions/43381922 / ... )
ishandutta2007

7

Якщо ви хочете попередньо підписатися на той самий Observable, який буде повернено, просто скористайтеся

.do ():

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
Армандо Переа

Це може бути гарною відповіддю, але насправді те, що ви нічого про це не пояснили, робить це поганою відповіддю. Що означає "попередня передплата"? І чи має це вирішувати питання з відкривачем ниток?
Флоріан Лейтгеб

зауважте, що в RxJS 6 doзараз викликається, tapі ви повинні використовувати його в трубі. Також зверніть увагу, що tapдля різних обробників, таких як next, completeта , використовується кілька параметрів error.
Simon_Weaver

7

Проблема в тому, що дані фіксуються всередині спостережуваного, і я можу просто консолірувати їх. Я хочу повернути це значення та console.log або що-небудь з іншого файлу, викликавши функцію, в якій він знаходиться.

Схоже, ви шукаєте "поточну величину", яка отримує всередині спостережуваного, коли воно випромінює та після випромінювання.

Subjectі Observableне має такого. Коли значення випускається, воно передається своїм абонентам і Observableвиконується з ним.

Ви можете використовувати те, BehaviorSubjectяке зберігає останню випущену вартість і негайно передає її новим абонентам.

Він також має getValue()метод отримання поточного значення;

Подальше читання:

Тема поведінки RxJS

Як отримати поточне значення RxJS Subject або Observable?


2

Спостережувані значення можна отримати з будь-якого місця. Послідовність джерел спочатку натискається на спеціального спостерігача, який може випромінювати в іншому місці. Це досягається за допомогою класу Subject з реактивних розширень (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

Хоча попередні відповіді можуть працювати по-різному, я думаю, що використання BehaviourSubject - це правильний спосіб, якщо ви хочете продовжувати використовувати спостережувані.

Приклад:

    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

У однопотоковому, асинхронному, перспективно-орієнтованому, реагуючому тренді світі javascript 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);
     }
  })();

Чому я маю використовувати асинхронність у випадку масиву даних?
Янош Вінслер

@JanosVinceller Це лише приклад для зосередження уваги на інших частинах коду. Ви можете замінити from(["someValue","someOtherValue"])спостережуваним на ваш вибір, який випромінює більше одного значення. Я думаю, це може бути більше підходящим щоб використали interval(1000).
Марінос

0

Пристойним способом було б повернути спостережуване з функції і підписатись на нього, де б це не було потрібно, оскільки спостережувані є лінивими, вони почнуть випускати значення лише тоді, коли вони підписані.

Тут у мене є ще одне цікаве рішення, кероване подіями, з яким я спочатку звикав. Наступний приклад робить це за допомогою модуля " подій " 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

Це лише приклад продемонструвати ідею, коли ми можемо передавати дані з різних місць методом випромінювання та прослуховування. Це також відомо як керований подіями код.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.