получение значения rx js в syn c, это антипаттерн? - PullRequest
1 голос
/ 29 мая 2020

Я часто обнаруживаю, что мне нужно просто получить значение из магазина и сделать что-то вроде этого, что кажется анти-паттерном (побочный эффект запоминания r), и задаюсь вопросом, есть ли лучший способ.

    getAssetName(i_page: IPager): string {
        let r = '';
        if (i_page.Player[0].$.src) {
            this.storeSelector.getScene(i_page.Player[0].$.src)
                .take(1)
                .subscribe((v) => {
                    r = v.Player.$.label;
                });
        } else {
            this.storeSelector.getResource(i_page.Player[0].Data[0].Resource[0].$.resource)
                .take(1)
                .subscribe((v) => {
                    r = v.resource_name;
                });
        }
        return r;
    }

другими словами, лучший способ получить значение 'r' без необходимости его закрывать?

сейчас я использую Angular и знаю, что могу использовать 'asyn c' как в

{{ getAssetName$(element.pager) | async | truncate:[maxStringSize]}}

this.getAssetName$ = this.getAssetName;


 getAssetName(i_page: IPager): Observable<string> {
        if (i_page.Player[0].$.src) {
            return this.storeSelector.getScene(i_page.Player[0].$.src)
                .map((v) => {
                    return v.Player.$.label;
                });
        } else {
            return this.storeSelector.getResource(i_page.Player[0].Data[0].Resource[0].$.resource)
                .map((v) => {
                    return v.resource_name;
                });
        }
    }

, но пытаюсь понять, есть ли способ обойтись без async шаблонного метода. Если я использую asyn c без onPu sh, то заметна деградация производительности шаблона

Спасибо,

Sean

Ответы [ 3 ]

1 голос
/ 29 мая 2020

Несколько способов go по этому поводу - ниже приведены три возможных решения.

Поскольку вы получаете только один результат, вы можете использовать .toPromise() как более чистый способ преобразования потока для возврата asyn c result.

async getAssetName(i_page: IPager): string {
  const assetSrc = (i_page.Player[0].$.src)
    ? this.storeSelector.getScene(i_page.Player[0].$.src).pipe(map(x => x.Player.$.label))
    : this.storeSelector.getResource(i_page.Player[0].Data[0].Resource[0].$.resource)
      .pipe(map(x => x.resource_name));
  return await assetSrc.pipe(take(1)).toPromise();
}

Если вы хотите полностью избежать асинхронного c и хотите получить имя как часть построения более крупного объекта, тогда это построение может быть чисто выполнено внутри сам поток.

template: MyTemplate;

private getAssetName$(i_page: IPager): Observable<string> {
  const assetSrc = (i_page.Player[0].$.src)
    ? this.storeSelector.getScene(i_page.Player[0].$.src).pipe(map(x => x.Player.$.label))
    : this.storeSelector.getResource(i_page.Player[0].Data[0].Resource[0].$.resource)
       .pipe(map(x => x.resource_name));
  return assetSrc.pipe(take(1));
}

createTemplate(i_page: IPager, /* other stuff */) {
  // you can skip the fork join if you're just using one observable.
  forkJoin(this.getAssetName(i_page), /* other observables if necessary*/).pipe(
    subscribe(([assetName, /* other results */]) => {
      this.template = { assetName, /* other stuff */ };
    })
  );
}

Я не большой поклонник делать это таким образом. Если возможно, я бы сделал сам шаблон наблюдаемым.

Чтобы построить шаблон, я объединяю все последние изменения из всех наблюдаемых, которые необходимы для построения. При смене пейджера тема увольняется. Эта тема связана с получением assetName в combLatest с помощью switchMap. Когда результат получен, шаблон $ Observable обновляется последним результатом.

private pageChangeSubject = new Subject<IPager>();
// like forkJoin the combineLatest is only needed if multiple observables.
// getAssetName is the same method from the previous example.
template$: Observable<MyTemplate> = this.combineLatest(
    this.pageChangeSubject.pipe(switchMap(x => this.getAssetName(x)))
  ).pipe(map(([assetName, /* ... */]) => ({ assetName, /* ... */ })));

pageChanged(page: IPager) { this.pageChangeSubject.next(page); }
1 голос
/ 29 мая 2020

Я предлагаю вам использовать BehaviourSubject , это вариант Subject, поэтому вы можете использовать его как наблюдаемое с помощью метода asObservable и получить значение в syn c с помощью метода getValue.

Пример:

public scene$: BehaviourSubject<any> = new BehaviourSubject(null); // needs a first value
public resource$: BehaviourSubject<any> = new BehaviourSubject(null);;

...
scene$.next(newValue); // update current value of scene

...
scene$.asObservable().subscribe(console.log) // async
scene$.getValue() // sync
0 голосов
/ 29 мая 2020

после оценки всего, я считаю, что это лучшее решение, которое следует использовать:

  1. Angular asyn c pipe
  2. onPu sh для уменьшения количества вызовов из шаблон для метода (важно)
  3. change Знак обнаружения для проверки при необходимости
  4. оператор очистки if

Решение:



<template>:
<td mat-cell *matCellDef="let element">
    {{getAssetNameAsync$(element) | async}}
</td>


<component>:
@Component({
    selector: 'app-scene-block-location',
    templateUrl: './block-location.component.html',
    styleUrls: ['./block-location.component.scss'],
    changeDetection: ChangeDetectionStrategy.OnPush
})

...

constructor(priate cd: ChangeDetectorRef) {}

someUpdateHappeningAtStore() {
    this.cd.markForCheck();
}


getAssetNameAsync$(i_page: IPager): Observable<string> {
     return (i_page.Player[0].$.src)
         ? 
         this.storeSelector.getScene(i_page.Player[0].$.src)
         .map(x => x.Player.$.label)
         :    
  this.storeSelector.getResource(i_page.Player[0].Data[0].Resource[0].$.resource)
      .map(x => x.resource_name)
      .take(1);
}



...