Knockout single observable с несколькими подписками

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

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

Используя следующий пример: (это очень урезано для краткости, в полном коде много логики, часть которой дублируется)

self.VisitDate = ko.observable();

self.VisitDate.subscribe(function (newValue) {
    self.ListItemRemoved(removed);
});   

self.VisitDate.subscribe(function (newValue) {
    self.Basket.VisitDate(newValue);
});

Я думал, что должен увидеть какую-то ошибку из-за нескольких подписок, но все работает нормально, но я не могу найти четкого объяснения, почему это нормально делать?

Я просто пытаюсь выяснить следующее:

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

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


person Reggie    schedule 29.05.2019    source источник
comment
шаблон observer/observable предназначен для поддержки нескольких подписок. Вся идея состоит в том, чтобы сначала разделить изменение и его эффекты, что позволит вам иметь любое количество эффектов. КО в этом плане ничего странного. Состояние гонки в значительной степени не является проблемой - JS будет запускать ваш код в одном потоке, а наблюдаемое все равно будет уведомлять каждого наблюдателя по одному.   -  person VLAZ    schedule 29.05.2019
comment
Я вижу, что, поскольку они находятся в 1 потоке, они будут выполняться один за другим, чтобы вторая подписка могла действовать на изменения, возникшие в результате выполнения первой подписки, я правильно понял?   -  person Reggie    schedule 29.05.2019


Ответы (1)


шаблон проектирования observer/observable допускает несколько наблюдателей/подписок. Цель шаблона проектирования вкратце такова:

  1. Отделите изменение от последствий изменения.
  2. Разрешить любые и произвольные эффекты, связанные с изменением.

Итак, Knockout делает это через свои наблюдаемые.

var observable = ko.observable("a");

observable.subscribe(function(newValue) {
  console.log("observer 1", newValue)
});

observable.subscribe(function(newValue) {
  console.log("observer 2", newValue)
});

observable.subscribe(function(newValue) {
  console.log("observer 3", newValue)
});

observable("b");
observable("c");
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>

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

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

for (subscriber of this.subscribers) {
  subscriber.update(this.value);
}

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

var observable = ko.observable("a");

var sharedState = "";

observable.subscribe(function(newValue) {
  //append to the shared state
  sharedState += newValue;
  console.log("observer 1", sharedState);
});

observable.subscribe(function(newValue) {
  //double up the shared state
  sharedState += sharedState;
  console.log("observer 2", sharedState);
});

observable("b");
observable("c");
observable("d");
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>

var observable = ko.observable("a");

var sharedState = "";


observable.subscribe(function(newValue) {
  //double up the shared state
  sharedState += sharedState;
  console.log("observer 2", sharedState);
});

observable.subscribe(function(newValue) {
  //append to the shared state
  sharedState += newValue;
  console.log("observer 1", sharedState);
});

observable("b");
observable("c");
observable("d");
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>

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

person VLAZ    schedule 29.05.2019