关于javascript:使用RxJS switchMap仅退订具有相同请求URL /操作有效负载(可观看Redux的史诗)的流

Using RxJS switchMap to only unsubscribe from streams with the same request URL/action payload (redux-observable epics)

我有一个界面,用户可以在该界面中触发对同一终结点但具有不同参数(在本例中为UUID)的调用。到目前为止,每当我分派具有相同类型的新redux操作时,我就一直享受switchMap取消运行中的HTTP请求的行为,在这种情况下,我仍然希望该行为,但前提是请求了新操作UUID(动作对象的一部分)与已经在进行中的UUID相同。我不确定这是否正确。

例如,在一次调度多个动作之后,我希望所有具有唯一ID的动作都已完成,但是那些重复现有ID且尚未完成的ID的动作将取消先前的请求并取而代之。

例如:

1
2
3
4
5
6
7
store.dispatch({ type:"GET_SOME_DATA", uuid:"1" })
store.dispatch({ type:"GET_SOME_DATA", uuid:"2" })
store.dispatch({ type:"GET_SOME_DATA", uuid:"2" })
store.dispatch({ type:"GET_SOME_DATA", uuid:"3" })
store.dispatch({ type:"GET_SOME_DATA", uuid:"2" })
// Get results back for '1', then '3', then '2' assuming equal response times.
// Only the duplicate uuid calls were cancelled, even though all have the same 'type'

我尝试使用.distinctUntilChanged((a, b) => a.uuid === b.uuid)将流的输入过滤到.switchMap中,只是为了查看会发生什么,但这仅限制了到达switchMap的动作,以及取消除最近GET_SOME_DATA动作以外的所有动作的行为API调用仍然发生。

1
2
3
4
5
6
7
8
9
const getDataEpic = (action$) =>
  action$.ofType(GET_SOME_DATA)
    .switchMap(({ uuid }) => // would be great if the switchMap would only cancel existing streams with same uuid
      ajax.getJSON(`/api/datastuff/${uuid}`)
        .map((data) => successAction(uuid, data.values))
        .catch((err) => Observable.of(
          errorAction(uuid),
          setNotificationAction((err.xhr.response && err.xhr.response.message) || 'That went wrong'),
        ))

目前,我正在使用mergeMap,但是我担心这可能会导致出现类似实时搜索的问题,其中较旧的请求可能会在最近的请求之后得到解决,从而导致我的redux存储更新为旧数据,因为mergeMap不会像switchMap那样取消Observable流...我有办法查看当前的RxJS Ajax请求并使用新操作的URL取消那些请求,还是我显然缺少的更好的解决方案?

干杯!

编辑:我想知道将switchMap更改为mergeMap,然后链接takeUntil并取消其他GET_SOME_DATA操作是否是一种正确的方法,或者那是否会使所有请求立即取消?
例如

1
2
3
4
5
6
7
8
9
10
11
12
const getDataEpic = (action$) =>
  action$.ofType(GET_SOME_DATA)
    .mergeMap(({ uuid }) =>
      ajax.getJSON(`/api/datastuff/${uuid}`)
        .takeUntil(
          action$.ofType(GET_SOME_DATA).filter(laterAction => laterAction.uuid === uuid)
        )
        .map((data) => successAction(uuid, data.values))
        .catch((err) => Observable.of(
          errorAction(uuid),
          setNotificationAction((err.xhr.response && err.xhr.response.message) || 'That went wrong'),
    ))

Edit2:显然takeUntil附加功能似乎正在工作!我不确定是否合适的程度是100%,但我希望得到一些反馈。我也想支持手动取消选项,这里讨论的方法是否正确实施?

Edit3:我认为这是我最终的工作版本。删除了mergeMap中Redux动作的解构,以防万一有人对redux-observables感到陌生:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const getDataEpic = (action$) =>
  action$.ofType(GET_SOME_DATA)
    .mergeMap((action) =>
      ajax.getJSON(`/api/datastuff/${action.uuid}`)
        .takeUntil(Observable.merge(
          action$.ofType(MANUALLY_CANCEL_GETTING_DATA)
            .filter((cancelAction) => cancelAction.uuid === action.uuid),
          action$.ofType(GET_SOME_DATA)
            .filter((laterAction) => laterAction.uuid === action.uuid),
        ))
        .map((data) => successAction(action.uuid, data.values))
        .catch((err) => Observable.of(
          errorAction(action.uuid),
          setNotificationAction((err.xhr.response && err.xhr.response.message) || 'That went wrong'),
    ))

从快速点击可见的一切中观察到的网络行为。只有非重复的id请求通过了!

enter image description here


您还可以使用groupBy运算符来处理具有相同uuid的流,并在每个uuid操作流上应用有用的switchMap行为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
action$.ofType(GET_SOME_DATA)
.groupBy(
    ({ uuid }) => uuid, // group all the actions by uuid
    x => x,
    group$ => group$.switchMap(_ => Observable.timer(5000)) // close existing streams if no event of a grouped action is emitted 5 seconds in a row (prevents memory leaks)
)
.mergeMap(actionsGroupedByUuid$ =>
    actionsGroupedByUuid$.switchMap(({ uuid }) =>
        ajax.getJSON(`/api/datastuff/${uuid}`)
            .map((data) => successAction(uuid, data.values))
            .catch((err) => Observable.of(
                errorAction(uuid),
                setNotificationAction((err.xhr.response && err.xhr.response.message) || 'That went wrong'),
            ))
    )
);