takeEvery 和 takeLatest。为什么?什么时候使用?同时使用?

takeEvery and takeLatest. Why? When to use? Use simultaneously?

我不清楚什么时候使用 takeEvery 什么时候使用 takeLatest ?在 redux-saga 中。

通过阅读官方文档,我得到了一个基本的区别。但是在 takeEvery 中创建并发操作有什么用(例如,用户快速连续点击 Load User 按钮 2 次,第二次点击将发送 USER_REQUESTED 而第一个触发的 fetchUser 尚未终止)

import { takeEvery } from `redux-saga/effects`

function* fetchUser(action) {
  ...
}

function* watchFetchUser() {
  yield takeEvery('USER_REQUESTED', fetchUser)
}

谁能解释一下。因为我对 redux-saga 完全陌生。

提前致谢。

这是您真正需要根据用例考虑的事情。

在某些情况下您可能会使用 takeEvery

  1. 对于非异步的传奇,因此没有理由 取消它们。 takeLatest 也可以在这里工作,但它可能会给 阅读代码时的错误指示 取消。

  2. 有时每次的操作在某些方面有所不同。例如。 假设您有一部电影,并且要添加类型为 电影。每次触发动作时,您都会获得不同的流派, 即使它是相同的操作类型。用户可以添加流派 比您从服务器获得响应更快。但就因为你 快速添加多种类型并不意味着您想停止传奇 添加上一个。

  3. 当您具有相同的加载操作时的廉价实现 多个不同的项目。例如。你有电影列表,每个都有 "load detail" 按钮。 (让我们忽略你应该 加载开始后可能会隐藏或禁用按钮)。这 一部电影的数据始终相同,但它们之间有所不同。 当您单击电影 1 的加载详细信息时,您不想取消 加载电影 2 的数据。因为它是电影的动态列表 动作类型每次都是一样的,区别会是 可能类似于电影中的动作 id

    在理想的实现中,您应该取消之前的加载传奇 相同的 movie/id,但这将需要更复杂的代码和 所以如果你只做一些简单的应用程序,你可能会决定忽略 那并且只允许 运行 同一部电影的传奇故事多次。 这就是为什么我称它为 "cheap implementation".

虽然@Martin Kadlec 的可靠回答涵盖了这个问题,但我想详细说明 takeEverytakeLatest[=101= 的细节和差异] 以及何时可能使用它们,以便您可以推导出它们的可能 use-cases。

TLDR:

当需要returns之前的所有任务时,您可以使用takeEvery。例如,在一定时期内从气象站获取温度和湿度数据,以将其存储在数据库中并显示为图表 - 在这种情况下,所有以前的传奇及其 return-values 是相互交集的,并且 不仅最新.

如果 i.E.,您可以使用 takeLatest internal/external 实例或接口的用户可以触发多个连续的操作,并且只需要最后一个值的结论。一个很好的例子是快速调用 broker-API 获取 live-ticker 获取 stock-values,其中只有 latest/most 最近的值感兴趣 .

详细:

takeEverytakeLatest 视为 redux-saga 的较低级别 API 之上的辅助函数当特定操作被分派到 Store 时,它​​包装了内部操作,例如生成任务。调用它们会在每个派发到与模式匹配的商店的动作上产生一个传奇。

takeEvery:

最常见的 takeEvery 函数在行为和方法上与 redux-thunk 非常相似。它基本上是模式或通道的 yield takeyield fork.

的包装器

关于takeEvery的线索是它允许启动一个已定义action/task的多个实例(例如下面示例中的fetchSomeThing)concurrently/simultaniously.

takeLatest 不同,您可以在 fetchSomeThing 的一个或多个先前实例尚未 completed/terminated 时开始新的 fetchSomeThing 任务,因此仍然悬而未决。请记住,不能保证任务 terminate/complete 会按照它们开始时的顺序进行。要处理乱序响应,您可以使用 takeLatest.

来自官方docs:

takeEvery(pattern, saga, ...args)

在发送到与模式匹配的 Store 的每个操作上生成一个 saga。

  • pattern:字符串 |数组 |函数
  • saga: 函数 - 生成器函数
  • args:数组 - 要传递给启动任务的参数。 takeEvery 会将传入的动作添加到参数列表中(即动作将是提供给 saga 的最后一个参数)

您还可以传入通道作为参数而不是模式,从而导致与 takeEvery(pattern, saga, ...args).[=28= 相同的行为]

取最新:

相比之下,takeLatest 辅助函数仅获取被触发的 最新请求 的响应,可以看作是对yield take 模式或通道 和额外的 if-statement 检查是否存在 lastTask(之前的任务是仍然悬而未决),然后将通过 yield cancel 和随后的 yield fork 终止,这将产生当前的 task/action.

来自官方docs:

takeLatest(pattern, saga, ...args)

只会得到最近触发的请求的响应。

  • pattern:字符串 |数组 |函数
  • saga: 函数 - 生成器函数
  • args:数组 - 要传递给启动任务的参数。 takeLatest 会将传入的动作添加到参数列表中(即动作将是提供给 saga 的最后一个参数)

takeEvery 类似,您也可以传入通道作为参数而不是模式。

总结一下,

  • takeEvery 允许处理并发操作。例如,用户快速连续点击加载用户按钮 2 次,第二次点击将发送一个 USER_REQUESTED 动作,而第一次点击时 fetchUser 尚未终止。
    takeEvery 不处理来自任务的乱序响应。无法保证任务将按照它们开始的相同顺序终止。要处理乱序响应,您可以考虑 takeLatest。
  • takeLatest 而不是在每个分派的 USER_REQUESTED 操作上启动一个新的 fetchUser 任务。由于 takeLatest 取消了 之前启动的任何待处理任务,我们确保如果用户快速触发多个连续的 USER_REQUESTED 操作,我们只会以最新的行动结束

文档:https://redux-saga.js.org/docs/api/

  • takeEvery - 允许同时使用多个 fetchData 对象。 在给定的时刻,我们可以启动一个新的 fetchData 任务 仍然是一个或多个以前的 fetchData 任务 尚未终止。

  • takeLatest - 在任何给定时刻只能激活一个 fetchData 任务。它 也将是最近开始的工作。如果一个新的 fetchData 作业在上一个任务仍在 运行 时启动, 之前的工作将立即终止