Приостановить эпопею, когда критерии соблюдены, затем выполнить буферизованное действие, когда критерии будут выполнены

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

В настоящее время мы делаем это, планируя действие «persist», которое распространяет упорядоченное количество событий, прежде чем закончить действие «persist_end». В настоящее время, если пользователь перемещается быстро, эти сгруппированные действия могут перехватывать друг друга, что приводит к различным проблемам. Я думал, что могу буферизовать начальное действие и дождаться выполнения конечного действия.

Я создал аналогичный пример, используя пример пинг-понга с сайта Redux-Observables: https://codepen.io/dualcyclone/pen/GOZRxW?editors=0011

const actionPauser = new BehaviorSubject(false);

const pingEpic = action$ =>
    action$.ofType(PING)
      .do(action => console.log(action)) // check if action caught by epic
      .buffer(actionPauser.asObservable().filter(paused => !paused))
      .do(eh => console.log('buffered? ',eh)) // check if buffered actions is occurring
      .do(() => actionPauser.next(true)) // tell pauser to pause
      .map((buf) => buf[buf.length-1])
      .filter(action => action !== undefined)
      .delay(1000)
      .mapTo({ type: PONG });

const pauseEpic = action$ =>
    action$.ofType(PONG)
      .delay(1000)
      .do(() => actionPauser.next(false)) // tell pauser to not pause
      .mapTo({ type: PING });

Предпосылка аналогична, я разрешаю пользователю нажимать кнопку «start PING» так часто, как они хотят, эпик, который это слушает, должен проверить, не происходит ли в данный момент действие ping (с помощью BehaviorSubject «actionPauser» ) и ставьте в очередь любые действия, пока не завершится предыдущее действие проверки связи.

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

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


person Luke Dyson    schedule 03.11.2017    source источник
comment
Похоже на оператор audit был бы вам полезен.   -  person cartant    schedule 03.11.2017
comment
@cartant Спасибо, ваше предложение сделало именно то, что мне нужно!   -  person Luke Dyson    schedule 06.11.2017


Ответы (2)


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

Аудит:

Игнорирует исходные значения на время, определяемое другим Observable, затем выдает самое последнее значение из исходного Observable, а затем повторяет этот процесс.

По сути, это позволяет мне игнорировать несколько генерируемых событий «PING», пока одно из них продолжается. Затем он продолжит выполнение последнего самого последнего события «PING», поэтому мы видим следующий результат:

(click) PING (click) PING (click) PING (click) PING PONG DONE PONG DONE

Первое и последнее действия PING - единственные, которые распространяются через Epic, поэтому мы видим два последних действия PONG, за которыми следует действие DONE.

Итак, вот пример, на который дан ответ (который также можно увидеть в моем коде здесь)

const pingEpic = action$ =>
  action$.ofType(PING)
    .audit(() => actionPauser.filter(paused => !paused))
    .do(() => actionPauser.next(true))
    .delay(1000)
    .mapTo({ type: PONG });

const pauseEpic = action$ =>
  action$.ofType(PONG)
    .delay(1000)
    .mapTo({ type: DONE })
    .do(() => actionPauser.next(false));
person Luke Dyson    schedule 06.11.2017

Похоже, concatMap может работать хорошо в этом случае.

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

Таким образом, в приведенном ниже случае одновременно работает только один таймер. Любые PING, поступившие, пока предыдущий все еще ожидает, будут помещены в буфер.

const pingEpic = action$ =>
  action$.ofType(PING)
    .concatMap(() =>
      Observable.timer(1000)
        .mapTo({ type: PONG })
    );

https://jsbin.com/gocezut/edit?js,output

(rapidly click four times)
PING
PING
PING
PING
(1000ms pass)
PONG
(1000ms pass)
PONG
(1000ms pass)
PONG
(1000ms pass)
PONG

Помните, что большинство вопросов, наблюдаемых с помощью redux, можно преобразовать в обычные вопросы RxJS, расширяя ресурсы и помощь, которые вы можете найти. В этом прелесть redux-observable: это почти полностью обычные шаблоны RxJS.

person jayphelps    schedule 04.11.2017
comment
Я попробую это сделать; но в моем конкретном случае ping указывает на начало последовательности других действий, последнее из которых завершает и вызывает действие pong. Итак, в вашем примере я потенциально мог бы отложить его до начала следующего действия, но в идеале мне нужно предотвратить следующий пинг, если понг не произошел. Итак, вывод должен быть: (щелкните) ping (щелкните) (щелкните) (щелкните) pong ping pong (в идеале я хочу отфильтровать, чтобы разрешалось распространение только самого последнего невыполненного действия) - person Luke Dyson; 06.11.2017
comment
чтобы уточнить, вы говорите, что хотите предотвратить / буферизовать PING? Это невозможно с помощью redux-observable (намеренно), потому что это означает изменение намерения действий, отправляемых из вашего пользовательского интерфейса. Вместо этого используйте намеренное действие, чтобы сигнализировать о начале потенциально других действий. Если это проясняет, вы можете переименовать PING во что-то вроде BUFFERED_PING, тогда эпик потенциально может немедленно отправить PING или дождаться, пока он не получит PONG, чтобы начать. - person jayphelps; 06.11.2017
comment
Это часто сбивает с толку людей, когда они относительно новички в саге о редукции / саге о редукции. Просто помните, что действия, отправляемые напрямую из пользовательского интерфейса, всегда должны иметь намерение, что может или не может сразу вызвать желаемый побочный эффект, в зависимости от желаемой бизнес-логики в ваших эпиках. - person jayphelps; 06.11.2017