RxJS разделяет родительский наблюдаемый среди разделенных дочерних наблюдаемых - PullRequest
0 голосов
/ 01 ноября 2018

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

Я сократил мою проблему до следующего кода (кстати, я не уверен, почему функция фрагмента SO не работает, поэтому я сделал CodePen , где вы можете попробовать мой код).

const { from, merge } = rxjs;
const { partition, share, tap } = rxjs.operators;

let hasAmmo = true;
const [ fire$, noAmmo$ ] = from([true]).pipe(
  share(),
  partition(() => hasAmmo),
);


merge(
  fire$.pipe(
    tap(() => {
      hasAmmo = false;
      console.log('boom');
    }),
  ),
  noAmmo$.pipe(
    tap(() => {
      console.log('bam');
    }),
  )
).subscribe({
  next: val => console.log('next', val),
  error: val => console.log('error', val),
  complete: val => console.log('complete', val),
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/6.3.3/rxjs.umd.js"></script>

Когда я запускаю этот код, я получаю следующее:

"boom"
"next" true
"bam"
"next" true
"complete" undefined

Я не понимаю, почему я получаю "bam".

Первое излучение идет к fire$ (я получаю "boom"), что имеет смысл, потому что hasAmmo равно true. Но побочным эффектом излучения fire$ является то, что в результате изменения состояния раздела я думаю, что я получаю "bam".

Разве я не должен вызывать побочные эффекты, которые влияют на partition()?

Или, может быть, есть проблема с тем, как я share() наблюдаю моего родителя? Я могу ошибаться, но я бы интуитивно подумал, что fire$ и noAmmo$ внутренне подписываются на родителя, чтобы разделить его, и в этом случае share() должно работать?

1 Ответ

0 голосов
/ 02 ноября 2018

Это на самом деле работает правильно. Путаница возникает из-за оператора partition, который в основном состоит из двух filter операторов .

Если переписать его без partition, оно будет выглядеть так:

const fire$ = from([true]).pipe(
  share(),
  filter(() => hasAmmo),
);

const noAmmo$ = from([true]).pipe(
  share(),
  filter(() => !hasAmmo),
);

Имейте в виду, что изменение hasAmmo не влияет на саму partition. partition действует только тогда, когда он получает значение от своего источника Observable.

При последующем использовании merge() он делает две отдельные подписки на две совершенно разные цепочки с двумя разными from([true]) с. Это означает, что true передается как fire$, так и noAmmo$.

Так что share() здесь никак не влияет. Если вы хотите поделиться им, вам придется обернуть from, прежде чем использовать его на fire$ и noAmmo$. Если наблюдаемый источник просто from, он, к сожалению, будет еще более запутанным, поскольку первоначальное излучение поступит только первому подписчику, который на fire$ позже при использовании в merge:

const shared$ = from([true]).pipe(
  share(),
);

const fire$ = shared$.pipe(...);
const noAmmo$ = shared$.pipe(...);

Последнее, почему вы получаете оба сообщения, это то, что partition не изменяет значение, которое проходит. Он только решает, какой из возвращаемых Обсерваторий переиздаст его.

Кстати, лучше избегать partition полностью, потому что это, вероятно, устареет, и просто используйте filter, что более очевидно:

...