使用 redux-observable 去抖动和取消 [英] Debouncing and cancelling with redux-observable

查看:86
本文介绍了使用 redux-observable 去抖动和取消的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am trying to create a simple redux-observable epic which debounces and is cancelable. My code:

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()]))
    ));
};

该代码仅有时有效.根据服务器的响应速度,我相信可能会出现 2 种情况中的一种.

The code only works sometimes. Depending on the speed of the response from the server, I believe 1 of 2 scenarios can occur.

场景 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 调用而无需等待?

Is there a way I can write my epic such that only the validateCancelAction can bypass the debounceTime and cancel the ajax call without waiting?

谢谢!

推荐答案

您实际上只是在消除您对 validateRequestAction 的匹配,但是您的 .takeUntil(action$.ofType(validateCancelAction)) 没有任何去抖动.我可能是错的,但如果取消操作有可能在 通过去抖动之前被调度,那么它要取消的操作将不会被取消,因为 ajax 请求甚至还没有开始,也没有 takeUntil.可以通过在副作用(在本例中为 ajax)实际开始并且 takeUntil 正在侦听可能的取消之前不允许取消来避免这种竞争.

You're actually only debouncing your matching of validateRequestAction, but your .takeUntil(action$.ofType(validateCancelAction)) does not have any debouncing. I may be wrong, but if it's possible for the cancel action to be dispatched before the action has made it past the debounce, then the action it was meant to cancel will not be cancelled because the ajax request hasn't even started yet, nor the takeUntil. This race can be avoided by not allowing a cancellation until your side effect (ajax in this case) has actually started and the takeUntil is listening for the possible cancellation.

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

In your UI you would not give the user the ability to cancel until some state in redux is set. Since our epic needs to tell redux when to flip that, we'll need to emit an action that we will listen for in the reducers.

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

The easiest way is use the startWith operator:

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 就会知道我们应该给他们取消的能力.

So in this example, some reducer would listen for validateRequestActionStarted and change some state that the UI will then know we should give them the ability to cancel.

一种完全不同的防止这种竞争的方法——但在大多数情况下我不推荐这种方法——将 takeUntil 完全放在顶级流上,然后重新启动"如果被取消,则使用 repeat 的史诗.因此,当我们取消时,这将关闭所有内容;任何待处理的 ajax 和任何待处理的去抖动.

A totally different way of preventing that race--but one I wouldn't recommend in most cases--would be to takeUntil on the top-level stream entirely and then just "restart" the epic using repeat if it gets cancelled. So this would shut down everything when we cancel; any pending ajaxs and any pending 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();
};

<小时>

值得注意的是,我使用了史诗和重启这两个术语来帮助概念化我们的特定领域,但这主要是普通的 RxJS,因此它通常适用于 redux-observable 之外.史诗"只是我们的函数模式的一个词,它接受动作流(输入)并返回动作流(输出).


It's worth noting that I used the terms epic and restart to help conceptualize our specific domain, but this is mostly just normal RxJS so it's generally applicable outside of redux-observable. An "epic" is just a word for our pattern of a function which takes a stream of actions (input) and returns a stream of actions (output).

这篇关于使用 redux-observable 去抖动和取消的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆