使用redux-observable进行去除和取消

问题描述 投票:5回答:2

我正在尝试创建一个简单的redux-observable史诗,它可以去抖动并且可以取消。我的代码:

export const apiValidate = action$ => {
    return action$.ofType(validateRequestAction)
        .debounceTime(250)
        .switchMap((action) => (
            Observable.ajax({
                url: url,
                method: 'GET',
                crossDomain: true,
                headers: {
                    "Content-Type": 'application/json'
                },
                responseType: 'json'
            })
           .map(payload => (new APISuccessValidate()))
           .takeUntil(action$.ofType(validateCancelAction))
           .catch(payload => ([new APIFailureValidate()]))
    ));
};

代码有时只能工作。根据服务器响应的速度,我相信可能会出现两种情况中的一种。

场景1(工作):

Time 0ms   - Fire validateRequestAction
Time 250ms - Ajax request occurs
Time 251ms - Fire validateCancelAction
Time 501ms - validateCancelAction passes debounce and cancels properly
Nothing else occurs

场景2(破碎)

Time 0ms   - Fire validateRequestAction
Time 250ms - Ajax request occurs
Time 251ms - Fire validateCancelAction
Time 400ms - Ajax returns, APISuccessValidate action fired
Time 501ms - validateCancelAction passes debounce and there is nothing to cancel

有没有办法可以编写我的史诗,只有validateCancelAction可以绕过debounceTime并取消ajax调用而无需等待?

谢谢!

rxjs redux-observable
2个回答
9
投票

你实际上只是在调整你的validateRequestAction的匹配,但是你的.takeUntil(action$.ofType(validateCancelAction))没有任何去抖动。我可能错了,但是如果在动作通过去抖动之前可以发送取消动作,那么它原本要取消的动作将不会被取消,因为ajax请求还没有开始,也没有takeUntil。通过不允许取消直到您的副作用(在这种情况下为ajax)实际开始并且takeUntil正在倾听可能的取消,可以避免这种竞赛。

在您的UI中,在设置redux中的某个状态之前,您不会让用户取消。由于我们的史诗需要告诉redux何时翻转它,我们需要发出一个我们将在reducers中监听的动作。

最简单的方法是使用startWith运算符:

export const apiValidate = action$ => {
    return action$.ofType(validateRequestAction)
        .debounceTime(250)
        .switchMap((action) => (
            Observable.ajax({
                url: url,
                method: 'GET',
                crossDomain: true,
                headers: {
                    "Content-Type": 'application/json'
                },
                responseType: 'json'
            })
          .map(payload => (new APISuccessValidate()))
          .takeUntil(action$.ofType(validateCancelAction))
          .catch(payload => ([new APIFailureValidate()]))
          .startWith({ type: 'validateRequestActionStarted' }) // <-- here
    ));
};

所以在这个例子中,一些reducer会监听validateRequestActionStarted并改变一些状态,UI会知道我们应该让他们能够取消。


防止这场比赛的完全不同的方式 - 但在大多数情况下我不建议 - 完全是对顶级流的takeUntil,然后使用repeat“重新启动”史诗如果被取消。因此,当我们取消时,这将关闭所有内容;任何未决的ajaxs和任何未决的debounces。

export const apiValidate = action$ => {
    return action$.ofType(validateRequestAction)
        .debounceTime(250)
        .switchMap((action) => (
            Observable.ajax({
                url: url,
                method: 'GET',
                crossDomain: true,
                headers: {
                    "Content-Type": 'application/json'
                },
                responseType: 'json'
            })
          .map(payload => (new APISuccessValidate()))
          .catch(payload => ([new APIFailureValidate()]))
        ))
        .takeUntil(action$.ofType(validateCancelAction))
        .repeat();
};

值得注意的是,我使用术语epic和restart来帮助概念化我们的特定域,但这通常只是普通的RxJS,所以它通常适用于redux-observable之外。 “史诗”只是我们的一个函数模式的一个词,它接受一系列动作(输入)并返回一个动作流(输出)。


2
投票

我假设您可能希望它有两种情况:

场景1:

您希望在收到取消操作时立即取消限制。这意味着您可能希望重置第二个流。这很好但可能不是你想要的。

action$ => {
  const requestAction$ = action$.pipe(
    ofType(validateRequestAction),
    share()
  )
  return merge(
    action$.pipe(
      ofType(validateCancelAction),
      mapTo(false)
    ),
    requestAction$.pipe(mapTo(true))
  ).pipe(
    distinctUntilChanged(),
    switchMap(
      condition => 
        condition ? 
          requestAction$.pipe(
            debounceTime(250),
            switchMap(query => sendRequest(query)
          ) : EMPTY
    )
  )

场景2:

您发送取消信号,同时告诉每个待处理的请求:“嘿,您不允许发送”。有两种方法可以做到这一点:

  • 第一个,使用请求操作以相同的延迟限制取消操作,以便它与请求操作流竞争。

码:

merge(
  action$.pipe(
    ofType(validateCancelAction),
    debounceTime(250),
    mapTo(undefined)
  ),
  action$.pipe(
    ofType(validateRequestAction),
    debounceTime(250),
    pluck('payload')
  )
).pipe(
  switchMap(
    query => query ? sendRequest(query) : of({ type: validateCancelDone })
  )
)
  • 第二个也是正确的解决方案是,在调度取消操作时,将状态设置为取消。每个受限制的操作都必须在允许发出任何请求之前检查此情况:

实际上,这只是您想要在流中还是在redux内存储已取消的状态。我打赌你选择第一个。码:

export default action$ => 
  combineLatest(
    action$.pipe(
      ofType(validateRequestAction),
      debounceTime(250),
      pluck('payload')
    ),
    merge(
      action$.pipe(
        ofType(validateCancelAction),
        mapTo(false)
      ),
      action$.pipe(
        ofType(validateRequestAction),
        mapTo(true)
      )
    ).pipe(
      distinctUntilChanged()
    )
  ).pipe(
    switchMap(
      ([query, allow]) =>
        allow
          ? sendRequest(query)
          : EMPTY
    )
  )

编辑:

你还需要distinctUntilChanged() allow流或debounceTime将不起作用。

© www.soinside.com 2019 - 2024. All rights reserved.