Оператор RxJs с безусловным выполнением в конце каждой итерации - PullRequest
0 голосов
/ 11 января 2019

Существует ли Rx-оператор или композиция, гарантирующая выполнение некоторой логики последней для каждого наблюдаемого излучения?

Давайте предположим следующий контекст:

  1. бесконечная или непрерывная последовательность
  2. условная логика типа filter() для пропуска некоторых выбросов
  3. некоторая логика в конце каждой итерации в doAlways() -подобном операторе

Пожалуйста, обратитесь к пронумерованным комментариям в примере кода ниже

Примечания:

  • finalize() потребует завершения последовательности (нарушает п. 1)
  • iif() или обычный if внутри switchMap() является опцией, но делает код более нечитаемым

Фрагмент кода для иллюстрации: Шаг (3) должен выполняться всегда, для каждой итерации, последним, т.е. мы хотим всегда начинать и заканчивать журнал в doAlways() -подобном операторе вместо tap()

import { of, interval } from 'rxjs'; 
import { tap, filter, switchMap } from 'rxjs/operators';


const dataService = { update: (z) => of(z /*posts data to back-end*/) };

const sub = interval(1000).pipe(            // <-- (1)
  tap(a => console.log('starting', a)),
  filter(b => b % 100 === 0),               // <-- (2)
  switchMap(c => dataService.update(c)),
  tap(d => console.log('finishing', d))     // <-- (3) should execute always even (2)
)
.subscribe(x => console.log(x));

Ответы [ 3 ]

0 голосов
/ 11 января 2019

Нет, то, что вы спрашиваете, невозможно из-за того, что отфильтрованные уведомления никогда не будут переданы. Вам понадобится уведомление совершенно нового типа, которое не используется ни одним оператором, кроме того, который вы описали.

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

const sub = interval(1000).pipe(            
  tap(a => console.log('starting', a)),
  mergeMap(b => b % 100 === 0 ? of(b) : throwError(b)),
  switchMap(c => dataService.update(c)),
  catchError(b => of(b)),
  tap(d => console.log('finishing', d))
)

Обратите внимание, что мы не используем filter, а сопоставляем либо с next, либо с error уведомлением в зависимости от условий. Ошибки, естественно, будут игнорироваться большинством операторов и потребляться tap, subscribe или catchError. Это способ поставить метку на предмет, чтобы работники знали, что им не следует прикасаться к ней (по аналогии с Инго)

0 голосов
/ 12 января 2019

Я бы разделил его на 2 потока с partition, и они бы слили их обратно.

https://stackblitz.com/edit/rxjs-8z9jit?file=index.ts

import { of, interval, merge } from 'rxjs';
import { tap, switchMap, partition, share } from 'rxjs/operators';

const dataService = { update: z => of(z /*posts data to back-end*/) };

const [sub, rest] = interval(1000).pipe(
  tap(a => console.log('starting', a)),
  share(),
  partition(b => b % 2 === 0)
);

merge(
    sub.pipe(
      switchMap(c => dataService.update(c)),
      tap(() => console.log('Ok'))
    ), 
    rest
)
.pipe(tap(d => console.log('finishing', d)))
.subscribe(x => console.log(x));
0 голосов
/ 11 января 2019

Такой оператор не существует, и это потому, что он не может существовать. После того как вы отфильтровали значение, полученное в результате наблюдаемое просто больше не излучает его. Любой оператор «вниз по течению» просто не знает о его существовании.

Чтобы проиллюстрировать это, вы включили switchMap в какой-либо сервис, который зависит от значения, которое выдается. По понятным причинам этот оператор не может быть применен логически, если нет значения для включения.

Вы должны были бы «пометить» каждое значение вместо того, чтобы фильтровать его и отложить фильтр до вызова касания, но даже тогда сценарии, такие как переключение на другое наблюдаемое, потребуют более подробных требований.


Думайте о наблюдаемом как о конвейерной ленте, на которую вы помещаете предметы. Каждый оператор - это комната, через которую ведет ремень. Внутри каждой комнаты рабочий может решить, что делать с каждым предметом: изменить его, убрать, положить вместо него новые предметы и т. Д. Однако каждый работник видит только то, что несет конвейер - они не знают, какие еще комнаты пришли до них или что там было сделано.

Чтобы достичь того, что вы хотите, рабочий в последней комнате должен был знать о предмете, который он никогда не получал, что потребовало бы дополнительных знаний, которых у него нет.

...