Як "почекати" двох спостережуваних у RxJS


76

У моєму додатку у мене є щось на зразок:

this._personService.getName(id)
      .concat(this._documentService.getDocument())
      .subscribe((response) => {
                  console.log(response)
                  this.showForm()
       });

 //Output: 
 // [getnameResult]
 // [getDocumentResult]

 // I want:
 // [getnameResult][getDocumentResult]

Тоді я отримую два розділені результати, спочатку з, _personServiceа потім з _documentService. Як я можу почекати, поки обидва результати перед викликом this.showForm()закінчаться, а потім маніпулювати результатами кожного з них.



наскільки я розумію, ти вже є, в силуconcat
user3743222

@ user3743222 У цьому випадку значення випромінюються окремо. Один за одним.
gog

1
in forkJoin 'subscribe отримає один результат - кортеж з першою та другою відповідями - це саме те, що ви запитали?
Юлія Пасинкова

Ви можете подивитися на forkJoin у моєму дописі в блозі - medium.com/@juliapassynkova/q-map-to-rxjs-981936a2b22d
Юлія Пасинкова

Відповіді:


107

Останнє оновлення: травень 2020 р.

combLatest (спостережувані)

З документації ReactiveX :

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

(Оновлення: травень 2020 р.) Хоча інший приклад залишається дійсним, ось новий синтаксис:

// Observables to combine
const name$ = this._personService.getName(id);
const document$ = this._documentService.getDocument();

name$.combineLatest(document$, (name, document) => {name, document})
    .subscribe(pair => {
           this.name = pair.name;
           this.document = pair.document;
           this.showForm();
       })

combLatest (спостережувані) (альтернативний синтаксис):

// Observables to combine
const name$ = this._personService.getName(id);
const document$ = this._documentService.getDocument();

combineLatest(name$, document$, (name, document) => ({name, document}))
    .subscribe(pair => {
           this.name = pair.name;
           this.document = pair.document;
           this.showForm();
       })

zip проти compileast

(Оновлення: жовтень 2018 р.) Раніше я пропонував використовуватиzipметод. Однак для деяких випадків використанняcombineLatestмає кілька переваг передzip. Тому важливо розуміти відмінності.

CombineLatestвипромінює останні випущені значення з спостережуваних. У той час як zipметод випускає випущені елементи в послідовності .

Наприклад, якщо спостережуваний # 1 випускає свій 3-й елемент, а спостережуваний # 2 випускає свій 5-й елемент. Результат із використанням zipметоду буде 3-м випущеним значенням обох observables.

У цій ситуації результат використання combineLatestбуде 5-м і 3-м . що відчувається більш природно.


Observable.zip (спостережувані)

(Оригінальна відповідь: липень 2017 р.) Метод Observable.zip пояснюється в документації reactiveX :

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

// Observables to combine
const name$ = this._personService.getName(id);
const document$ = this._documentService.getDocument();

Observable
    .zip(name$, document$, (name: string, document: string) => ({name, document}))
    .subscribe(pair => {
           this.name = pair.name;
           this.document = pair.document;
           this.showForm();
       })

додаткова примітка (застосовується для обох методів)

Останній параметр, де ми надали функцію, (name: string, document: string) => ({name, document})є необов’язковим. Ви можете пропустити це або зробити більш складні операції:

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

Отже, якщо пропустити останню частину, ви отримаєте масив:

// Observables to combine
const name$ = this._personService.getName(id);
const document$ = this._documentService.getDocument();

Observable
    .zip(name$, document$)
    .subscribe(pair => {
           this.name = pair['0'];
           this.document = pair['1'];
           this.showForm();
       })

Чи можна дочекатися завершення одного спостережуваного з цим?, У мого спостережуваного є інший внутрішній спостережуваний, який, у свою чергу, покладається на http.get?
HDJEMAI

Чому у вашому оновленні за січень 2020 року навіщо мапірувати масив об’єкту? Здається, непотрібний крок, коли ви можете знищити масив у методі передплати. Це був би мій єдиний коментар, решта виглядає добре.
Крейг Майлз,

OOOOHHHH, я шукав комбайнаОстанній оператор протягом години ... велике спасибі
Ендрю

Як ви скасуєте підписку на об’єднані спостережувані дані? Просто скасуйте підписку на спостережуване, яке повертається з kombinacijiLatest (). Subscribe ()?
sharon182

@ sharon182 точно.
Хамід Асгарі

36

Використовуйте forkJoin()метод спостережуваних. Перевірте це посилання для довідки

З документів RXJS

Цей оператор найкраще використовувати, коли у вас є група спостережуваних, і ви дбаєте лише про остаточне значення кожного з них. Одним із найпоширеніших випадків використання цього є те, що ви бажаєте надсилати кілька запитів при завантаженні сторінки (або якоїсь іншої події) і хочете вжити заходів лише тоді, коли відповідь отримана для всіх. Таким чином, це схоже на те, як ви можете використовувати Promise.all

Observable.forkJoin([character, characterHomeworld]).subscribe(results => {
  // results[0] is our character
  // results[1] is our character homeworld
  results[0].homeworld = results[1];
  this.loadedCharacter = results[0];
});

Код взято з: https://coryrylan.com/blog/angular-multiple-http-requests-with-rxjs


Чи можна дочекатися завершення одного спостережуваного з цим?, У мого спостережуваного є інший внутрішній спостережуваний, який, у свою чергу, покладається на http.get?
HDJEMAI,

@HDJEMAI Якщо ти хочеш зробити щось. після завершення одного спостережуваного, чи можете ви використовувати передплатну підписку?
Yuwei HE

16

У RxJS Оператори для чайників: forkJoin, застібка - блискавка, combineLatest, withLatestFrom мені дуже допоміг. Як зазначено в назві, воно описує такі комбінаційні оператори:

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


Дякую, що перше посилання, яке ви дали, тобто "Оператори RxJs ...", є обов'язковим для прочитання та найкращим та найпростішим поясненням, яке ви могли коли-небудь отримати
Рам,

5

Удосконалення відповіді Хаміда Асгарі, який використовує прямі декомпозиції аргументів і автоматично додає типи (коли ви використовуєте машинопис)

const name$ = this._personService.getName(id);
const document$ = this._documentService.getDocument();

combineLatest([name$, document$]).subscribe(([name, document]) => {
    this.name = name;
    this.document = document;
    this.showForm();
});

БОНУС : Ви також можете обробляти помилки, використовуючи наведений вище підхід, наступним чином


2

Для мене цей зразок був найкращим рішенням.

const source = Observable.interval(500);
const example = source.sample(Observable.interval(2000));
const subscribe = example.subscribe(val => console.log('sample', val));

Отже .. лише коли друге (приклад) випромінює - ви побачите останнє випущене значення першого (джерела).

У своєму завданні я чекаю перевірки форми та інших подій DOM.


1

Погляньте на метод 'combLatest', він може бути тут доречним. http://reactivex.io/rxjs/class/es6/Observable.js~Observable.html#static-method-combineLatest

const { Observable } = Rx

const name$ = this._personService.getName(id);
const document$ = this._documentService.getDocument();

Observable
    .combineLatest(name$, document$, (name, document) => ({ name, document }))
    .first() // or not, implementation detail
    .subscribe(({ name, document }) => {
        // here we have both name and document
        this.showForm()
    })

0

Ви можете використовувати 'zip' або 'buffer', як показано нижче.

function getName() {
    return Observable.of('some name').delay(100);
}

function getDocument() {
    return Observable.of('some document').delay(200);
}

// CASE1 : concurrent requests
Observable.zip(getName(), getDocument(), (name, document) => {
    return `${name}-${document}`;
})
    .subscribe(value => console.log(`concurrent: ${value}`));

// CASE2 : sequential requests
getName().concat(getDocument())
    .bufferCount(2)
    .map(values => `${values[0]}-${values[1]}`)
    .subscribe(value => console.log(`sequential: ${value}`));
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.