redux - 使用惯用的 redux observable 排队和取消事件

标签 redux rxjs rxjs5 redux-observable

以下载管理器为例。可以有任意数量的事件下载。

可以调度操作来启动、停止、标记完成并标记任何特定下载的下载进度。

const START_DOWNLOAD = "START_DOWNLOAD";
const startDownload = payload => ({ type: START_DOWNLOAD, payload });

const DOWNLOAD_PROGRESS = "DOWNLOAD_PROGRESS";
const downloadProgress = payload => ({ type: DOWNLOAD_PROGRESS, payload });

const STOP_DOWNLOAD = "STOP_DOWNLOAD";
const stopDownload = payload => ({ type: STOP_DOWNLOAD, payload });

const COMPLETE_DOWNLOAD = "COMPLETE_DOWNLOAD";
const completeDownload = payload => ({ type: COMPLETE_DOWNLOAD payload });

这些操作将包含一个标识下载的 id,并且可以使用以下 reducer 修改 redux 状态:

const downloadReducer = (state = initialState, action) => {
  switch (action.type) {
    case STOP_DOWNLOAD:
      return {
        ...state,
        [action.payload.id]: {
          state: "IDLE",
        },
      };

    case START_DOWNLOAD:
      return {
        ...state,
        [action.payload.id]: {
          state: "IN_PROGRESS",
          progress: 0,
        },
      };

    case DOWNLOAD_PROGRESS:
      return {
        ...state,
        [action.payload.id]: {
          state: "IN_PROGRESS",
          progress: action.payload.progress,
        },
      };

    case COMPLETE_DOWNLOAD:
      return {
        ...state,
        [action.payload.id]: {
          state: "DONE",
          progress: 100,
        },
      };

    default:
      return state;
  }
};

现在是关于如何使用 redux observable 管理这些操作的异步调度的问题。

例如我们可以这样做:

const downloadEpic = action$ =>
  action$.ofType(START_DOWNLOAD).mergeMap(action =>
    downloader
    .takeUntil(
      action$.filter(
        stop =>
        stop.type === STOP_DOWNLOAD &&
        stop.payload.id === action.payload.id,
      ),
    )
    .map(progress => {
      if (progress === 100) {
        return completeDownload({
          id: action.payload.id
        });
      } else {
        return downloadProgress({
          id: action.payload.id,
          progress
        });
      }
    }),
  );

这行得通。但是,如果我们想限制允许的事件下载数量怎么办。我们可以替换 mergeMapconcatMap一次只允许一个事件下载。或者我们可以提供concurrent parametermergeMap并准确指定我们希望一次允许执行多少次内部下载器 observable。

但是,这带来了我们现在无法停止排队下载的问题。

I have created a complete working example that you can try here.

如何以最惯用的方式使用 rxjs 和 redux observable 限制和排队下载?

最佳答案

几天来我一直在关注这个问题,但没有时间用完整的代码给你一个可靠的答案,因为它相对涉及。所以相反,我只会给你 tl;dr 版本,我希望它总比没有好:)

我的直觉告诉我,我会让 UI 发送一个表示尝试下载而不是真正保证的 Action 。例如ATTEMPT_DOWNLOAD 。史诗会监听这个 Action 并检查当前的事件下载数量是否超过> =最大值,如果是,将发出一个 Action 来排队下载而不是开始下载。

您的 reducer 将存储那些事件的下载 ID 和那些排队的下载 ID。例如

{ active: ['123', '456', ...etc], queued: ['789'] }

您既可以使用它来专门跟踪事件/排队,也可以了解它们的计数 active.lengthqueued.length
当下载完成时,某处的史诗会检查是否有任何排队的下载,如果有,则将其出列。你如何做到这一点主要是个人喜好。例如如果史诗发出 DEQUEUE_DOWNLOAD 或其他什么。

如果调度了取消操作,您的 reducer 将需要同时查看 activequeued 并删除其中存在的 ID。如果它实际上是事件的并且不仅仅是排队,那么正在处理下载的史诗将监听取消操作,它将停止它并执行与上述相同的出队检查。

这有点摇摆不定,但主要的收获是:
  • 你的 UI 组件在尝试之前不应该知道事情将如何或何时排队,尽管它可以通过查看 redux 状态(例如,显示“下载排队”或其他什么)来了解事后情况
  • 注意不要在多个地方重复下载状态。例如如果它处于事件状态,则您的商店中只有一个事实来源表明它是
  • 史诗在 reducer 之后运行,利用它来发挥你的优势。
  • 可能会有一个史诗,他唯一的工作就是监听真正的下载请求并执行它,而不知道排队或类似的东西。然后,这个史诗将被多个其他史诗重用。通过直接调用它作为一个函数或发出一个 Action ,如你的真实START_DOWNLOAD

  • 业务逻辑应该放在哪里并不总是很清楚。例如reducer 应该主要是 getter/setter 还是他们应该有更多的自以为是的逻辑并做出决定?在这些情况下没有很多规则。试着保持一致。

    顺便说一句,这完全来自我的直觉。我可能已经发现(或者您可能仍然会发现)在实践中这不是一个好的解决方案。只是给出我最初的想法!

    关于redux - 使用惯用的 redux observable 排队和取消事件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47687819/

    相关文章:

    react-native - 使用react-navigation时redux状态不发送

    rxjs - 合并 Observables 数组的结果

    javascript - Observable.create(...).map 不是函数

    javascript - 像 monad 一样处理流

    javascript - 为什么我的 rxjs combineLatest 输出只有 2 次?

    javascript - 如何取消执行新 Action 时执行的先前 Action ?

    reactjs - 为什么我会收到错误模块未找到 : Can't resolve 'redux'

    android - React Native API 实现导致错误

    javascript - 链二选择 rxjs store in Angular Guard

    javascript - RxJS Interval 无延迟