Идиоматический способ восстановления из потока onError

Отказ от ответственности: это продолжение предыдущего вопроса Безопасное обновление для 2 зависимых потоков

Каков идиоматический способ обработки ошибок в RxJS (или любой другой реализации RX), который позволяет потоку не прерываться?

Соответствующий код

function convert(unit, value) {
    var request = {};
    request[unit] = value;

    var conversion = $.ajax({
        method: 'POST',
        url: './convert.php',
        data: request,
        dataType: 'json'
    }).promise();

    return Rx.Observable.fromPromise(conversion).takeUntil(inInput.merge(cmInput));
}

var cmElement = document.getElementById('cm'),
    inElement = document.getElementById('in');

var cmInput = Rx.Observable.fromEvent(cmElement, 'input').map(targetValue),
    inInput = Rx.Observable.fromEvent(inElement, 'input').map(targetValue);

var inches = cmInput
    .flatMap(convert.bind(null, 'cm'))
    .startWith(0);

var centimeters = inInput
    .flatMap(convert.bind(null, 'in'))
    .startWith(0);

Итак, как видите, мы используем поток изменений поля ввода и пропускаем его через функцию convert, которая конвертирует его в другую единицу и передает результат дальше.

Если возникает ошибка во время вызова $.ajax(), то она распространяется вверх и весь поток inches или cetimeters останавливается (на самом деле это ожидаемо).

Но как бы мне это реализовать, чтобы этого не делать?

Чтобы я мог изящно обрабатывать ошибки, например показывать сообщение об ошибке и повторять попытку при поступлении новых данных?

Моя текущая идея состоит в том, чтобы ввести составной тип, такой как Data.Either в Haskell, и передавать его потоком вместо скалярных двойников.

Мысли?

UPD: Да, я прочитал Обработка исключений в реактивных расширениях без остановки последовательности, но все же надеюсь, есть лучшие способы.


person zerkms    schedule 24.12.2014    source источник
comment
Что такое targetValue?   -  person Brandon    schedule 30.12.2014


Ответы (2)


У вас действительно есть два варианта:

  1. Как вы говорите, верните некоторую форму Either, которая может быть либо результатом, либо ошибкой.

Поскольку это JavaScript, вам, очевидно, не нужен формальный тип, и вы можете просто передать экземпляры Error вместе с числами, и ваш подписчик сможет отличить их друг от друга, когда он их получит, проверив тип времени выполнения полученного значения. Так что это так же просто, как добавить .catch(function (e) { return Rx.Observable.of(e); } после вашего вызова .fromPromise (или вместо .promise() использовать .then() с фильтром ошибок, чтобы создать обещание, которое будет иметь любое значение, которое вы хотите, когда возникает ошибка).

  1. Отправляйте ошибки в отдельный поток.

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

function convert(errorObserver, unit, value) {
    ...
    return Rx.Observable
        .fromPromise(conversion)
        .catch(function (e) {
            errorObserver.onNext(e); // or whatever you want to emit here
            return Rx.Observable.empty(); // or possibly Rx.Observable.of(0) to reset?
        })
        ...
}

Затем просто создайте Subject для потока ошибок и укажите его в качестве первого аргумента convert. Или создайте 2 темы, если вы хотите, чтобы ошибки cm отделялись от ошибок in.

Я лично склоняюсь к первому способу.

person Brandon    schedule 30.12.2014
comment
Я лично склоняюсь к первому способу. --- я тоже. Это тоже была моя первоначальная идея - создать крошечную either-promise библиотечную оболочку для преобразования любого промиса в разрешенный с Either в результате - person zerkms; 05.01.2015
comment
Звучит как отличная идея и полезная, даже если вы просто используете промисы без Rx. - person Brandon; 05.01.2015

Вы можете просто добавить функцию catch() в цепочку fromPromise().

    return Rx.Observable.fromPromise(conversion).catch(handleError).takeUntil(inInput.merge(cmInput));

function handleError() {
    //Do whatever you want to handle this exception then return empty.
    return Rx.Observable.Empty();
}
person Jonathan Sheely    schedule 24.12.2014
comment
И теперь у подписчиков нет возможности обрабатывать ошибки. Так что, если сейчас ajax-запрос не удался - вы не можете показать соответствующее сообщение. Технически подойдет, практически - вряд ли надо выбирать. - person zerkms; 24.12.2014