Когда мне следует создать новую подписку для указанного побочного эффекта c? - PullRequest
10 голосов
/ 17 февраля 2020

На прошлой неделе я ответил на вопрос Rx JS , где я вступил в дискуссию с другим членом сообщества на тему: «Должен ли я создать подписку для каждого указанного c побочного эффекта или попытаться минимизировать подписки вообще? Я хочу знать, какую методологию использовать с точки зрения полного реактивного подхода к применению или когда переключаться с одного на другой. Это поможет мне и, возможно, другим избежать ненужных обсуждений.

Информация о настройке

  • Все примеры приведены в TypeScript
  • . вопрос без использования жизненных циклов / конструкторов для подписок и для сохранения в структуре несвязанных
    • Представьте себе: подписки добавляются в init конструктора / жизненного цикла
    • Представьте себе: отмена подписки выполняется в уничтожении жизненного цикла

Что такое побочный эффект (Angular образец)

  • Обновление / ввод в пользовательском интерфейсе (например, value$ | async)
  • Выход / восходящий поток компонента (например, @Output event = event$)
  • Взаимодействие между различными службами в разных иерархиях

Пример использования:

  • Две функции: foo: () => void; bar: (arg: any) => void
  • Две исходные наблюдаемые: http$: Observable<any>; click$: Observable<void>
  • foo вызывается после выдачи http$ и не требует значения
  • bar вызывается после click$, но требуется текущее значение http$

Дело: Создать подписку для каждого указанного c побочного эффекта

const foo$ = http$.pipe(
  mapTo(void 0)
);

const bar$ = http$.pipe(
  switchMap(httpValue => click$.pipe(
    mapTo(httpValue)
  )
);

foo$.subscribe(foo);
bar$.subscribe(bar);

Дело: Минимизировать подписки в целом

http$.pipe(
  tap(() => foo()),
  switchMap(httpValue => click$.pipe(
    mapTo(httpValue )
  )
).subscribe(bar);

Мое собственное мнение вкратце

Я могу понять тот факт, что подписки вначале делают ландшафты Rx более сложными, потому что вы должны думать о том, как подписчики должны влиять на канал или нет (например, поделиться своими наблюдаемый или нет). Но чем больше вы отделяете свой код (чем больше вы сосредотачиваетесь: что происходит, когда), тем легче поддерживать (тестировать, отлаживать, обновлять) ваш код в будущем. Имея это в виду, я всегда создаю один наблюдаемый источник и одну подписку для любого побочного эффекта в моем коде. Если два или более моих побочных эффектов вызваны одним и тем же наблюдаемым источником, тогда я делюсь своим наблюдаемым и подписываюсь на каждый побочный эффект индивидуально, потому что он может иметь разные жизненные циклы.

Ответы [ 2 ]

6 голосов
/ 21 февраля 2020

Rx JS является ценным ресурсом для управления асинхронными операциями и должен использоваться для упрощения вашего кода (включая сокращение количества подписок), где это возможно. Точно так же за наблюдаемой не должна автоматически следовать подписка на эту наблюдаемую, если Rx JS предоставляет решение, которое может уменьшить общее количество подписок в вашем приложении.

Однако в некоторых ситуациях это может полезно создать подписку, которая не является строго «необходимой»:

Пример исключения - повторное использование наблюдаемых в одном шаблоне

Глядя на ваш первый пример:

// Component:

this.value$ = this.store$.pipe(select(selectValue));

// Template:

<div>{{value$ | async}}</div>

Если значение $ используется только один раз в шаблоне, я бы воспользовался каналом asyn c и его преимуществами для экономии кода и автоматической отмены подписки c. Однако согласно этому ответу следует избегать нескольких ссылок на одну и ту же переменную asyn c в шаблоне, например:

// It works, but don't do this...

<ul *ngIf="value$ | async">
    <li *ngFor="let val of value$ | async">{{val}}</li>
</ul>

В этой ситуации вместо этого я бы создал отдельную подписаться и использовать это, чтобы обновить переменную non-asyn c в моем компоненте:

// Component

valueSub: Subscription;
value: number[];

ngOnInit() {
    this.valueSub = this.store$.pipe(select(selectValue)).subscribe(response => this.value = response);
}

ngOnDestroy() {
    this.valueSub.unsubscribe();
}

// Template

<ul *ngIf="value">
    <li *ngFor="let val of value">{{val}}</li>
</ul>

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

Принимая во внимание роль и продолжительность жизни наблюдаемого, прежде чем принимать решение о подписке

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

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

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

Относительно отписок:

A точка против дополнительных подписок заключается в том, что требуется больше отписок . Как вы сказали, мы хотели бы предположить, что все необходимые отписки применяются на Дестрой, но реальная жизнь не всегда так гладко! Опять же, Rx JS предоставляет полезные инструменты (например, first () ) для оптимизации этого процесса, что упрощает код и уменьшает вероятность утечек памяти. Эта статья содержит соответствующую дополнительную информацию и примеры, которые могут иметь значение.

Личные предпочтения / многословие по сравнению с краткостью:

Примите ваши собственные предпочтения во внимание. Я не хочу отклоняться от общей дискуссии о многословности кода, но цель должна состоять в том, чтобы найти правильный баланс между слишком большим «шумом» и чрезмерным шифрованием вашего кода c. Это может стоить посмотреть .

2 голосов
/ 22 февраля 2020

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

 const obs1$ = src1$.pipe(tap(effect1))
 const obs2$ = src2$pipe(tap(effect2))
 merge(obs1$, obs2$).subscribe()

Исключительно выполнение побочных эффектов при нажатии и активация с помощью слияния означают, что вы только когда-либо имели одна подписка.

Одна из причин не делать этого - то, что вы кастрируете большую часть того, что делает Rx JS полезным. Который является способностью составлять наблюдаемые потоки и подписываться / отписываться от потоков по мере необходимости.

Я бы сказал, что ваши наблюдаемые должны быть логически составлены, а не загрязнены или запутаны во имя сокращения подписок. Должен ли эффект foo логически сочетаться с эффектом бара? Нужно ли одно другому? Могу ли я когда-нибудь не захотеть запускать foo, когда http $ выбрасывает? Я создаю ненужную связь между несвязанными функциями? Это все причины, чтобы не помещать их в один поток.

Это даже не относится к обработке ошибок, которой проще управлять с несколькими подписками IMO

...