Как избежать глюков в Rx

В отличие от других библиотек "FRP", Rx не предотвращает сбои: обратные вызовы, вызываемые с несовпадающими во времени данными. Есть ли хороший способ обойти это?

В качестве примера представьте, что у нас есть ряд дорогих вычислений, полученных из одного потока (например, вместо _.identity, ниже, мы делаем сортировку или выборку ajax). Мы делаем отдельные UntilChanged, чтобы избежать пересчета дорогих вещей.

sub = new Rx.Subject();
a = sub.distinctUntilChanged().share();
b = a.select(_.identity).distinctUntilChanged().share();
c = b.select(_.identity).distinctUntilChanged();
d = Rx.Observable.combineLatest(a, b, c, function () { return _.toArray(arguments); });
d.subscribe(console.log.bind(console));
sub.onNext('a');
sub.onNext('b');

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

Этот конкретный пример можно обойти, отбросив DifferentUntilChanged и написав некоторые функции wonky scan() для прохождения через предыдущий результат, если входные данные не изменились. Затем вы можете сжать результаты, вместо того, чтобы использовать combLatest. Это неуклюже, но выполнимо.

Однако если где-либо существует асинхронность, например, вызов ajax, то zip не работает: вызов ajax завершится либо синхронно (если кэшируется), либо асинхронно, поэтому вы не можете использовать zip.

редактировать

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

У вас есть два потока, а и б. б зависит от а. b является асинхронным, но браузер может его кэшировать, поэтому он может обновляться независимо от a или одновременно с a. Таким образом, конкретное событие в браузере может вызвать одну из трех вещей: обновления; б обновления; и А и В обновление. Желаемое поведение - вызывать обратный вызов (например, метод рендеринга) ровно один раз во всех трех случаях.

zip не работает, потому что когда a или b срабатывает один, мы не получаем обратного вызова от zip. Объединение последнего не работает, потому что когда a и b срабатывают вместе, мы получаем два обратных вызова.

1 ответ

Решение

Концепт

и А и В обновление

где оба a а также b являются наблюдаемыми, не существует как примитив в Rx.

Не существует общего оператора без потерь, который можно определить для принятия решения о получении уведомления от a должен ли он передавать его вниз по потоку или удерживать, пока не получит уведомление от b, Уведомления в Rx изначально не содержат "обе" семантики или какой-либо семантики помимо грамматики Rx в этом отношении.

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

См. §§4.2, 6.7 в Руководстве по проектированию Rx.

Таким образом, то, что я имел в виду под "общим оператором без потерь, который может быть определен...", состоит в том, что даны две наблюдаемые a а также b с независимыми уведомлениями любой оператор, который пытается решить, когда он получает уведомление от a или же b должен ли он немедленно нажимать или ждать "другого" значения, должен полагаться на произвольные моменты времени. Это догадки. Таким образом, этот гипотетический оператор должен либо отбрасывать значения (например, DistinctUntilChanged или же Throttle) или время отбрасывания (например, Zip или же Buffer), хотя, вероятно, какая-то комбинация обоих.

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

Требуется тип с 3 состояниями: a | б | {А, Ь}

(Пожалуйста, извините моего паршивого JS)

var ab = function(a, b) { this.a = a; this.b = b; }
sub.onNext(new ab('a'));        // process a alone
sub.onNext(new ab('a', 'b'));   // process a and b together
sub.onNext(new ab(null, 'c'));  // process c alone

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

Кстати, спасибо за то, что вы предоставили простое объяснение в вашей редакции (мне все равно это понятно). Я впервые услышал о "глюках" в этом обсуждении на форуме Rx. Как видите, это никогда не было действительно завершено. Теперь мне интересно, действительно ли проблема этого ОП была такой простой, если предположить, что я, конечно, правильно понял вашу проблему.:-)

Обновить:

Вот еще одно связанное обсуждение, включая еще несколько моих мыслей о том, почему Rx не является FRP:

https://social.msdn.microsoft.com/Forums/en-US/bc2c4b71-c97b-428e-ad71-324055a3cd03/another-discussion-on-glitches-and-rx?forum=rx

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