Rxjs skip sample при первом emit, дождитесь завершения одного observable

Вопрос 1

 combineLatest(this.layerService.layersData$, this.displayService.displayData$, this.dataSource.data$,
      (layer, display, data) => ({ layer, display, data }))
      .pipe(
        skipWhile(({ layer, display, data }) =>
          _.isEmpty(layer) || _.isEmpty(display) || _.isEmpty(data)),
        takeWhile(() => this.cacheService.isDirty()),
        sample(interval(2000)),
        map(result => {
          const layerFiltered = result.layer.filter(ly => result.display.findIndex(d => d.id === ly.id) !== -1);
          return { ...result, layer: layerFiltered };
        })
  )
  .subscribe(result => {
    console.log(result);
  });

Я хочу избежать семплирования при самом первом испускании и использовать семплирование после этого.

Под самым первым излучением я имею в виду, что он смог добраться до функции карты. Можно ли этого добиться без использования внешней локальной переменной?


вопрос 2

ngOnInit() {
   this.displayService.displayData$.delay(500).take(1).subscribe(data =>  this.displayMeta = data);

   this.layerService.getLayerData()
     .subscribe(layers => {
       this.layers = layers;
     });
}

Я хочу, чтобы LayerService подписался, чтобы дождаться завершения displayService, я могу поместить логику подписки LayerService внутри метода подписки displayService, но это не кажется хорошим решением проблемы.

Я хочу, чтобы код this.displayService ....... был синхронным. Мне это тоже потребовалось один раз, а не оператор take (1).


Вопрос 3

dirty = {};
fetchedData = {};
reportData$ = new BehaviorSubject({});

constructor(private dataSourceService: DataSourceService, private someService: SomeService) {
  const dataFetch$ = this.dataSourceService.data$
    .pipe(
      tap(dList => {
        // update dirty by comparing dList, if this.dirty has 3 keys and dList have two item then this.dirty length will be two
        this.dirty = dList.reduce((acc, et) => ({ ...acc, [et.id]: _.get(this.dirty, et.id, true) }), {});
      }),
      filter(dList => !_.isEmpty(dList)),
      map(dList => _.filter(dList, dL => this.dataSourceService.dirty[dL.id])),
      concatMap(dList => from(dList)),
      flatMap(dItem => this.someService.getDataFromApi(dItem), (item, data) => {
        return { id: item.id, data };
      }),
      tap(({ id, data }) => {
        this.fetchedData[id] = data;
        this.dirty[id] = false;
        this.dataSourceService.resetDirty(id);
      })
    );

  dataFetch$.merge(this.dataSourceService.data$)
    .subscribe(() => {
      this.fetchedData = _.pickBy(this.fetchedData, (__, key) => _.has(this.dirty, key));
      this.reportData$.next(this.fetchedData);
    });
}

Метод подписки должен вызываться, даже если фильтр возвращает false. Проблема с описанным выше подходом заключается в том, что метод subscribe будет вызываться дважды.

Если dList пуст, dataFetch $ не вызывается, поэтому подписка вызывается один раз, но если она не пуста, подписка вызывается дважды.


Конструкция заключается в том, что если элемент удаляется из this.dataSourceService.data $ один за другим и, наконец, this.dataSourceService.data $ .length становится 0, наблюдаемая цепочка не достигает подписки, в этом случае также сделайте this.fetchedData = empty

Поскольку элемент из dataSourceService.data $ удаляется, соответствующий элемент из this.fetchedData должен быть удален, я не знаю, какой элемент удален, поэтому грязный флаг, обратите внимание на первую операцию касания. В подписке dirtyList используется для обновления fetchedData.

0
0
2 490
1

Ответы 1

Вопрос 1

Возможно, вы захотите создать Observable, похожий на тот, который у вас уже есть, но без оператора sample. Когда у вас есть такой Observable, у вас есть базовый строительный блок, который позволяет вам попасть туда, куда вы хотите.

Базовый Observable должен выглядеть примерно так

const basicObs = combineLatest(this.layerService.layersData$, this.displayService.displayData$, this.dataSource.data$,
      (layer, display, data) => ({ layer, display, data }))
      .pipe(
        skipWhile(({ layer, display, data }) =>
          _.isEmpty(layer) || _.isEmpty(display) || _.isEmpty(data)),
        takeWhile(() => this.cacheService.isDirty()),
        map(result => {
          const layerFiltered = result.layer.filter(ly => result.display.findIndex(d => d.id === ly.id) !== -1);
          return { ...result, layer: layerFiltered };
        })
  )

Затем вы можете просто объединить первое излучение basicObs со всеми последующими выбросами, используя оператор concat. Код будет выглядеть так

const firstNotification = basicObs.pipe(
  take(1)
);
const followingNotifications = basicObs.pipe(
  skip(1), // to avoid emitting the first element
  sample(interval(2000))
);

firstNotification.pipe(
  concat(followingNotifications)
)
.subscribe(result => console.log(result))

вопрос 2

Если вы хотите, чтобы подписка this.displayService.displayData$.delay(500).take(1) была выполнена до того, как будет выполнена подписка this.layerService.getLayerData(), то вы можете не попробовать что-то вроде этого

ngOnInit() {
   this.displayService.displayData$.pipe(
      delay(500),
      take(1),
      tap(data =>  this.displayMeta = data),  // this is the side effect that you have with the first subscription
      switchMap(() => this.layerService.getLayerData())
   )
     .subscribe(layers => {
       this.layers = layers;
     });
}

Ключевой идеей здесь является использование switchMap для переключения с первого Observable, как только он излучает, на второй. Однако перед переключением через оператора tap мы запускаем побочный эффект, который был встроен в подписку this.displayService.displayData$.delay(500).take(1).

что, если на второй вопрос часть подписки огромна.

vito 18.09.2018 17:52

я добавил еще один вопрос

vito 18.09.2018 18:04

что вы имеете в виду под "огромной частью подписки"?

Picci 20.09.2018 07:31

почему бы вам просто не удалить filter?

Picci 20.09.2018 07:32

часть подписки - это огромная сумма, если она не является тривиальной, и не перестанет ли switchMap прослушивать после одного раунда, поскольку есть take (1)

vito 21.09.2018 05:25

у меня есть связанный вопрос stackoverflow.com/questions/52412091/rxjs-if-else-logic

vito 21.09.2018 05:36

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