Angular RxJS объединяет синхронизацию, асинхронность и forkJoin в одном потоке Subject

У меня довольно сложное реактивное поведение, которого я хотел бы добиться с помощью RxJS, но пока не нашел подходящего решения.

В моем приложении angular, которое можно найти в Plnkr, у меня есть серия синхронного, асинхронного и параллельного асинхронного выполнения.

  1. Сначала пользователь нажимает кнопку, которая вызывает следующий мой RxJS Subject. userClick$ = new Subject<void>();.

    <button (click) = "model.on = !model.on; userClick$.next()"> {{ model.on ? 'Stop' : 'Start' }} </button>

  2. затем я предоставляю случайное число, используя getRandomNumber
  3. затем я вычисляю экспоненту этого случайного числа с помощью getExpNumber
  4. Наконец, мне нужно запустить getFloor и getCeil параллельно.

Что мне нужно?

  • Уметь использовать forkJoin в последних двух параллельных выполнениях # 4.
  • Уметь выразить зависимость # 3 от # 2 и # 4 от # 3:
    • getExpNumber зависит от getRandomNumber и запускается после него
    • getFloor работает параллельно с getCeil, и оба зависят от getExpNumber.
  • Уметь использовать возвращаемое значение потока в каждом из моих flatMap, в настоящее время я получаю только последнее значение (мне нужно отобразить случайное значение (первое flatMap) пользователю, используя канал ngFor и async.

Зависимость: я могу добиться этого, проверяя тип моего текущего наблюдаемого значения в каждом flatMap, но я думаю, что должен быть лучший способ.

Полный код:

import { Component, NgModule, VERSION } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { Observable } from 'rxjs/Observable';
import { Subject } from 'rxjs/Subject';
import 'rxjs/add/operator/filter';
import 'rxjs/add/operator/do';
import 'rxjs/add/operator/switchMap';

@Component({
  selector: 'my-app',
  template: `
    <button (click) = "model.on = !model.on; userClick$.next()">{{ model.on ? 'Stop' : 'Start' }}</button>
    <h1>Numbers stream</h1>
    <div *ngFor = "let numberValue of (numbers$ | async)">
      <h2>{{ numberValue }}</h2>
    </div>
    <label>{{ model.log }}</label>
  `,
})
export class App {
  model = {
    on: false,
    log: ''
  }
  userClick$ = new Subject<void>();
  numbers$: Observable<number[]> = this.userClick$
    .filter(() => !!this.model.on)
    .do(() => this.model.log = '')
    .switchMap(() => this.getRandomNumber())
    .switchMap((num) => this.getExpNumber(num))
    .switchMap((num) => this.getFloor(num))
    .switchMap((num) => this.getCeil(num));
  constructor() {
  }
  /**
   * Runs after user click
   */
  getRandomNumber(): Observable<number[]> {
    return new Observable<number[]>(observer => {
      this.model.log += ' getRandomNumber';
      observer.next([Math.floor(Math.random() * 10) + 1]);

      return () => {}
    }
  }
  /**
   * Depends on getRandomNumber and run after it
   */
  getExpNumber(n: number): Observable<number[]> {
    return new Observable<number[]>(observer => {
      this.model.log += ' getExpNumber';
      observer.next([Math.floor(Math.exp(n)]);

      return () => {}
    }
  }
  /**
   * Runs in parallel with getCeil
   */
  getFloor(n: number): Observable<number[]> {
    return new Observable<number[]>(observer => {
      this.model.log += ' getFloor';
      observer.next([Math.floor(n)]);

      return () => {}
    }
  }
  /**
   * Runs in parallel with getFloor
   */
  getCeil(n: number): Observable<number[]> {
    return new Observable<number[]>(observer => {
      this.model.log += ' getCeil';
      observer.next([Math.ceil(n)]);

      return () => {}
    }
  }
}
@NgModule({
  imports: [ BrowserModule ],
  declarations: [ App ],
  bootstrap: [ App ]
})
export class AppModule {}
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Angular и React для вашего проекта веб-разработки?
Angular и React для вашего проекта веб-разработки?
Когда дело доходит до веб-разработки, выбор правильного front-end фреймворка имеет решающее значение. Angular и React - два самых популярных...
Эпизод 23/17: Twitter Space о будущем Angular, Tiny Conf
Эпизод 23/17: Twitter Space о будущем Angular, Tiny Conf
Мы провели Twitter Space, обсудив несколько проблем, связанных с последними дополнениями в Angular. Также прошла Angular Tiny Conf с 25 докладами.
Угловой продивер
Угловой продивер
Оригинал этой статьи на турецком языке. ChatGPT используется только для перевода на английский язык.
Мое недавнее углубление в Angular
Мое недавнее углубление в Angular
Недавно я провел некоторое время, изучая фреймворк Angular, и я хотел поделиться своим опытом со всеми вами. Как человек, который любит глубоко...
Освоение Observables и Subjects в Rxjs:
Освоение Observables и Subjects в Rxjs:
Давайте начнем с основ и постепенно перейдем к более продвинутым концепциям в RxJS в Angular
1
0
1 812
2

Ответы 2

для меня странно, как вы создаете наблюдаемые (я буду использовать "of"), и я получил число, а не массив, поэтому код похож на

@Component({
  selector: 'my-app',
  template: `
    <button (click) = "model.on = !model.on; userClick$.next()">{{ model.on ? 'Stop' : 'Start' }}</button>
    <h1>Numbers stream</h1>
    <h2 *ngFor = "let numberValue of (numbers$ |async)">{{numberValue}}</h2>
    <label>{{ model.log }}</label>
  `,
})
export class HomeComponent {
  model = {
    on: false,
    log: ''
  }
  userClick$ = new Subject<void>();
  numbers$: Observable<number[]> = this.userClick$
    .switchMap(() => {
      return this.getRandomNumber().switchMap((num: number) => {
        this.model.log += num;
        return this.getExpNumber(num).switchMap((num2: number) => {
          this.model.log += num2;
          return forkJoin(this.getFloor(num2), this.getCeil(num2))
        })
      })
    })


  getRandomNumber(): Observable<number> {
    this.model.log += ' getRandomNumber';
    return of((Math.random() * 10) + 1);
  }
  getExpNumber(n: number): Observable<number> {
    this.model.log += ' getExpNumber';
    return of(Math.exp(n));
  }
  getFloor(n: number): Observable<number> {
    this.model.log += ' getFloor';
    return of((Math.floor(n)));
  }

  getCeil(n: number): Observable<number> {
    this.model.log += ' getCeil';
    return of(Math.ceil(n));
  }
}
  1. Не используйте "do", do - для проверки ответа
  2. Если я сделаю наблюдаемое, как вы, forkJoin у меня не сработает (вызовите функцию, но я не могу подписаться на них)

Интересное решение, позволяющее использовать forkJoin Спасибо @Eliseo. Теперь мне нужно найти чистый способ вернуть только значение из getRandomNumber в мой поток numbers$.

Royalsmed 16.03.2018 06:54

Если вы хотите только getRandomNumber, вы можете подписаться только на getRandomNumber.

this.getRandomNumber().subscribe((value:number)=>{console.info(value)}) 

ПРИМЕЧАНИЕ: когда мы пишем

<div *ngFor = "let value of ($numbers |async)>{{value}}</div>

Как"

//in .html
<div *ngFor = "let value of numberList>{{value}}</div>
//in .ts
numberList:number[]
subscription:Subscription;

ngOnInit()
{
  this.subscription=$numbers.subscribe(values:number[]=>{
      this.numberList=values;
      this.subscription.unsubscribe()
  }
}

Другие вопросы по теме