Эпос, игнорирующий действие до завершения первого запроса

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

Первоначально я думал, что мы можем использовать skipUntil(), но потом понял, что результат skipUntil() проглатывается и никогда не передается остальной части цепочки.

Ниже приведен примерный JS-код, показывающий, чего мы надеемся достичь.

  const fetchUserEpic = action$ =>
  action$.ofType(FETCH_USER)
    .skipUntil( /* first request completes */ )
    .mergeMap(action =>
      ajax.getJSON(`/api/users/${action.payload}`)
        .map(response => fetchUserFulfilled(response))
        .takeUntil(action$.ofType(FETCH_USER_CANCELLED))
    );

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

Спасибо за любые предложения.


person jachenry    schedule 05.01.2017    source источник


Ответы (2)


Предполагая, что вы полностью уверены в том, что вам нужно (обычно это не так, отбрасывание ввода во время выполнения предыдущего запроса означает, что результат устарел, switchMap и / или debounce обычно предпочтительны), take(1) + repeat должен работать, поскольку наблюдаемое действие является горячим .

const fetchUserEpic = action$ =>
  action$.ofType(FETCH_USER)
    .take(1)
    .concatMap(action =>
      ajax.getJSON(`/api/users/${action.payload}`)
        .map(response => fetchUserFulfilled(response))
        .takeUntil(action$.ofType(FETCH_USER_CANCELLED))
    )
    .repeat()

Вот фрагмент, имитирующий логику.

// simulate action$.ofType(FETCH_USER) every second
const obs1 = Rx.Observable.interval(1000).publish()
obs1.connect()

function getStuff(i) {
  return Rx.Observable.timer(i*1000).mapTo(i)
}

obs1.take(1).concatMap(getStuff).repeat().subscribe(console.log)
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.0.2/Rx.js"></script>

person Gluck    schedule 05.01.2017
comment
Спасибо. Я попробую. Сценарий использования, о котором мы думаем, - это когда пользователь быстро нажимает кнопку отправки или пытается обновить данные до того, как исходный ответ сможет вернуться. - person jachenry; 05.01.2017
comment
Возможно, отключение кнопки отправки, пока запрос находится в полете, является лучшим / более безопасным вариантом (в зависимости от состояния, установленного в FETCH_USER, сбрасывается в FETCH_FULLFILLED / CANCELED) - person Gluck; 05.01.2017

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

const fetchUserEpic = action$ =>
  action$.ofType(FETCH_USER)
    .exhaustMap(action =>
      ajax.getJSON(`/api/users/${action.payload}`)
        .map(response => fetchUserFulfilled(response))
        .takeUntil(action$.ofType(FETCH_USER_CANCELLED))
    );
person paulpdaniels    schedule 05.01.2017
comment
@jachenry extractMap определенно самый идиоматичный - person jayphelps; 06.01.2017
comment
@jayphelphs Знаете ли вы, есть ли эквивалент для rxjava? Не отображается в документации. - person jachenry; 06.01.2017
comment
@jachenry нет эквивалента. Нужен пользовательский оператор. - person jayphelps; 06.01.2017
comment
Извините, когда я прочитал ваш исходный пост, я не понял, что этот вопрос на самом деле для RxJava, потому что вы используете тот же шаблон redux-observable в пользовательской реализации на основе Java - что, кстати, чертовски круто. Если вы когда-нибудь откроете исходный код, дайте мне ссылку! - person jayphelps; 06.01.2017