javascript 如何避免在调度过程中调度

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/30357120/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-28 12:05:01  来源:igfitidea点击:

How to avoid dispatching in the middle of a dispatch

javascriptreactjsreactjs-flux

提问by Ian Walker-Sperber

Within my Flux architected React application I am retrieving data from a store, and would like to create an action to request that information if it does not exist. However I am running into an error where the dispatcher is already dispatching.

在我的 Flux 架构的 React 应用程序中,我正在从存储中检索数据,并且希望创建一个操作来请求该信息(如果该信息不存在)。但是,我遇到了调度员已经在调度的错误。

My desired code is something like:

我想要的代码是这样的:

getAll: function(options) {
  options = options || {};
  var key = JSON.stringify(options);
  var ratings = _data.ratings[key];

  if (!ratings) {
    RatingActions.fetchAll(options);
  }

  return ratings || [];
}

However intermittently fails when the dispatcher is already dispatching an action, with the message Invariant Violation: Dispatch.dispatch(...): Cannot dispatch in the middle of a dispatch.. I am often making requests in response to a change in application state (eg date range). My component where I make the request, in response to a change event from the AppStorehas the following:

但是,当调度程序已经在调度操作时会间歇性地失败,并显示消息Invariant Violation: Dispatch.dispatch(...): Cannot dispatch in the middle of a dispatch.. 我经常提出请求以响应应用程序状态的变化(例如日期范围)。为了响应来自 的更改事件,我发出请求的组件AppStore具有以下内容:

getStateFromStores: function() {
  var dateOptions = {
    startDate: AppStore.getStartISOString(),
    endDate: AppStore.getEndISOString()
  };

  return {
    ratings: RatingStore.getAll(dateOptions),
  };
},

I am aware that event chaining is a Flux antipattern, but I am unsure what architecture is better for retrieving data when it does not yet exist. Currently I am using this terrible hack:

我知道事件链是一种 Flux 反模式,但我不确定当数据尚不存在时哪种架构更适合检索数据。目前我正在使用这个可怕的黑客:

getAll: function(options) {
  options = options || {};
  var key = JSON.stringify(options);
  var ratings = _data.ratings[key];

  if (!ratings) {
    setTimeout(function() {
      if (!RatingActions.dispatcher.isDispatching()) {
        RatingActions.fetchAll(options);
      }
    }, 0);
  }

  return ratings || [];
},

What would be a better architecture, that avoids event chaining or the dispatcher error? Is this really event chaining? I just want to change the data based on the parameters the application has set.

什么是更好的架构,可以避免事件链或调度程序错误?这真的是事件链吗?我只想根据应用程序设置的参数更改数据。

Thanks!

谢谢!

回答by andykenward

You can use Flux waitFor()function instead of a setTimeout

您可以使用 FluxwaitFor()函数代替 setTimeout

For example you have 2 stores registered to the same dispatcher and have one store waitFor the other store to process the action first then the one waiting can update after and dispatch the change event. See Flux docs example

例如,您有 2 个商店注册到同一个调度程序,并且有一个商店等待另一家商店首先处理操作,然后等待的商店可以在更新并调度更改事件之后。请参阅 Flux 文档示例

回答by Ian Walker-Sperber

My particular error was occurring because my stores emitted their change event during the action dispatch, while it was still cycling through the listeners. This meant any listeners (ie components) that then triggered an action due to a data change in the store would interrupt the dispatch. I fixed it by emitting the change event after the dispatch had completed.

我的特定错误发生是因为我的商店在动作分派期间发出了它们的更改事件,而它仍在通过侦听器循环。这意味着由于存储中的数据更改而触发操作的任何侦听器(即组件)都将中断调度。我通过在调度完成后发出更改事件来修复它。

So this:

所以这:

this.emit(CHANGE_EVENT);

Became

成为

var self = this;

setTimeout(function() { // Run after dispatcher has finished
  self.emit(CHANGE_EVENT);
}, 0);

Still a little hacky (will probably rewrite so doesn't require a setTimeout). Open to solutions that address the architectural problem, rather than this implementation detail.

仍然有点hacky(可能会重写,因此不需要setTimeout)。开放解决架构问题的解决方案,而不是这个实现细节。

回答by Hannes Johansson

The reason you get a dispatch in the middle of a previous dispatch, is that your store dispatches an action (invokes an action creator) synchronouslyin the handler for another action. The dispatcher is technically dispatching until all its registered callbacks have been executed. So, if you dispatch a new action from either of the registered callbacks, you'll get that error.

您在上一次调度中间获得调度的原因是您的商店在处理程序中为另一个操作同步调度一个操作(调用操作创建者)。调度程序在技术上一直在调度,直到其所有注册的回调都已执行。因此,如果您从任一已注册的回调中分派新操作,则会收到该错误。

However, if you do some async work, e.g. make an ajax request, you can still dispatch an action in the ajax callbacks, or the async callback generally. This works, because as soon as the async function has been invoked, it per definition immediately continues the execution of the function and puts the callback on the event queue.

但是,如果您执行一些异步工作,例如发出 ajax 请求,您仍然可以在 ajax 回调或异步回调中分派一个动作。这是有效的,因为一旦调用了异步函数,它就会根据定义立即继续执行函数并将回调放在事件队列中。

As pointed out by Amida and in the comments of that answer, it's a matter of choice whether to make ajax requests from the store in response to an action, or whether to do it in the store. The key is that a store should only mutate its state in response to an action, not in an ajax/async callback.

正如 Amida 以及在该答案的评论中所指出的那样,是选择从商店发出 ajax 请求以响应操作还是在商店中执行此操作是一个选择问题。关键是 store 应该只在响应 action 时改变它的状态,而不是在 ajax/async 回调中。

In your particular case, this would be exemplified by something like this for your store's registered callback, if you prefer to make the ajax calls from the store:

在您的特定情况下,如果您更喜欢从商店进行 ajax 调用,那么对于您商店的注册回调,这将是这样的例子:

onGetAll: function(options) {
    // ...do some work

    request(ajaxOptions) // example for some promise-based ajax lib
        .then(function(data) {
             getAllSuccessAction(data); // run after dispatch
        })
        .error(function(data) {
             getAllFailedAction(data); // run after dispatch
        });

     // this will be immediately run during getAllAction dispatch
     return this.state[options];
},
onGetAllSuccess: function(data) {
    // update state or something and then trigger change event, or whatever
},    
onGetAllFailed: function(data) {
    // handle failure somehow
}

Or you can just put the ajax call in your action creator and dispatch the "success/failed" actions from there.

或者您可以将 ajax 调用放在您的动作创建者中,然后从那里发送“成功/失败”的动作。

回答by abhisekpaul

you can user the "defer" option in the dispatcher. In your case it would be like:

您可以使用调度程序中的“延迟”选项。在你的情况下,它会是这样的:

RatingActions.fetchAll.defer(options);

回答by Kev

In my case, I fetch data through the actions/actions creators. The store is only a dump place that receives the payload of an action.

就我而言,我通过操作/操作创建者获取数据。存储只是一个接收操作有效负载的转储场所。

This means that I would "fetchall" in an action and then pass the result to the store which will do whatever with it and then emit a change event.

这意味着我会在一个动作中“fetchall”,然后将结果传递给 store,它会用它做任何事情,然后发出一个更改事件。

Some people consider using stores like me, others think like you. Some people at Facebook uses "my" approach:

有些人考虑像我一样使用商店,其他人则像你一样思考。Facebook 的一些人使用“我的”方法:

https://github.com/facebook/flux/blob/19a24975462234ddc583ad740354e115c20b881d/examples/flux-chat/js/utils/ChatWebAPIUtils.js#L51

https://github.com/facebook/flux/blob/19a24975462234ddc583ad740354e115c20b881d/examples/flux-chat/js/utils/ChatWebAPIUtils.js#L51

I think it would probably avoid the dispatch problem treating your stores like this, but I may be wrong.

我认为这样处理您的商店可能会避免调度问题,但我可能错了。

An interesting discussion is this one: https://groups.google.com/forum/#!topic/reactjs/jBPHH4Q-8Sc

一个有趣的讨论是:https: //groups.google.com/forum/#!topic/reactjs/jBPHH4Q-8Sc

where Jing Chen (Facebook engineer) explains what she thinks about how to use stores.

Jing Chen(Facebook 工程师)在这里解释了她对如何使用商店的看法。