从 Redux 源码角度理解 Redux-thunk、Redux-promise、Redux-saga

Redux 是一个用于 JavaScript 应用程序的预测性状态容器,它可以让您的应用行为一致且易于测试。Redux 可以帮助您管理各种状态,并确保状态变更是可控的和可预测的。但是,Redux 自身只提供了最基本的 API,如 createStore()、dispatch() 和 getState()。为了扩展 Redux 并处理异步操作,社区开发了许多中间件,Redux-thunk、Redux-promise 和 Redux-saga 就是其中的代表。

在本文中,我们将深入研究这三个流行的 Redux 中间件,弄清楚它们的工作原理以及它们如何与 Redux 一起工作。同时,我们将从 Redux 源码的角度出发,逐步学习这些中间件的实现原理。

Redux-thunk

在 Redux-thunk 中间件诞生之前,处理异步操作的最基本方法是在 Action 中返回一个函数,而不是一个对象。这个函数的参数是 dispatch() 方法。

举个例子,我们在 Redux 中定义了一个 Action:

------ ----- --------- - -------- -- -
  ------ ---------- -- -
    ---------- ----- -------------- ---
    ------ -------------------
      ---------- -- -
        ---------- ----- --------------------- -------- ---- ---
      --
      ------------ -- -
        ---------- ----- ------------------- -------- ----- ---
      ---
  --
--

在上面的代码中,fetchUser() 返回了一个函数,这个函数接收 dispatch() 方法作为参数。这个函数会异步获取用户数据,并在获取数据成功或失败后分别调用 dispatch() 方法来分发相应的 Action。

这种模式称为 thunk:一个返回函数的函数。Redux-thunk 就是一个中间件,可以将这种模式嵌入到 Redux 中。

实现原理

Redux-thunk 本质上是一个函数,它的作用是将异步操作延迟到 Action Creator 中。当一个 Action Creator 返回一个函数时,Redux-thunk 会执行这个函数并传入 dispatch() 方法和 getState() 方法。这个函数会在内部执行异步操作,并在完成后调用 dispatch() 方法来分发相应的 Action。

----- ----- - -- --------- -------- -- -- ---- -- ------ -- -
  -- ------- ------ --- ----------- -
    ------ ---------------- ----------
  -
  ------ -------------
--

以上是 Redux-thunk 中间件的代码实现。它接收一个 store 对象,并返回一个函数,这个函数负责处理 Action。当 store.dispatch() 方法被调用时,这个函数会被调用,并先检查传入的 action 是否为函数。如果 action 是函数,thunk 中间件会立刻执行这个函数,并将 store.dispatch() 和 store.getState() 作为参数传入。

案例分析

假设我们有一个 todoList 应用,用户可以在应用中添加、完成、删除待办事项。每当用户进行这些操作时,我们需要在数据库中同步数据。

我们可以使用 Redux-thunk 捕获用户添加待办事项的操作,并在操作完成后调用 API 同步数据。

------ ----- ---- --------

------ ----- ------- - ------ -- ----- ---------- -- -
  ---------- ----- ------------------ ---
  --- -
    ----- ------------------- - ---- ---
    ---------- ----- ------------------- -------- - ---- - ---
  - ----- ------- -
    ---------- ----- ------------------- -------- - ----- - ---
  -
--

在上面的代码中,我们使用了 async 函数来处理异步操作,并在操作完成后调用 dispatch() 方法来分发相应的 Action。

需要注意的是,Redux-thunk 只是一种“手动”处理异步操作的方式,因此我们需要自己管理异步操作的状态。同时,由于异步 Action 及其对应的 reducer 可能会非常复杂,因此在处理异步操作时仍需要谨慎。

Redux-promise

Redux-promise 是另一个处理异步 Action 的 Redux 中间件。它在传递给 dispatch() 方法的 Action 中提供了一个序列化的 Promise。

这个 Promise 表示异步操作的结果,可以在 reducer 中进行处理。

实现原理

与 Redux-thunk 类似,Redux-promise 也是一个函数,它的作用是将 Promise 嵌入到 Redux 中。当一个 Action Creator 返回一个 Promise 时,Redux-promise 会等待这个 Promise 完成,并分发相应的 Action。

----- ------- - -- -------- -- -- ---- -- ------ -- -
  -- ------- ----------- --- ----------- -
    ------ ----------------------
  -
  ------ -------------
--

以上是 Redux-promise 中间件的代码实现,与 Redux-thunk 非常相似。当 store.dispatch() 方法被调用时,这个函数会被调用,并先检查传入的 action 是否为 Promise。如果 action 是 Promise,Redux-promise 中间件会立刻等待这个 Promise 完成,并将完成后的结果作为参数调用 dispatch() 方法进行 Action 分发。

案例分析

假设我们有一个 weather app 应用,我们需要在异步获取天气数据后,将数据作为 payload 发送到 reducer 中。

我们可以使用 Redux-promise 在 Action 中返回一个 Promise,然后在 reducer 中处理异步 Action。

------ ----- ------------ - -- -- --
  ----- ----------------
  -------- ---------------------
---

在上面的代码中,我们定义了一个 Action Creator,它返回了一个包含 axios.get() 的 Promise。当这个 Promise 完成后,Redux-promise 中间件会调用 dispatch() 方法来分发相应的 Action,并将完成的结果传递给 reducer。

比起 Redux-thunk,Redux-promise 更加简单,并且可以根据返回的 Promise 自动处理异步 Action。因此,与 Redux-thunk 不同,我们不需要在 Action Creator 自己处理异步操作,也不需要在 reducer 中额外的处理。但相应的,Redux-promise 的灵活性则变得比较有限。

Redux-saga

Redux-saga 是一个中间件,可以用于处理异步操作和副作用。相比之前的 Redux-thunk 和 Redux-promise,Redux-saga 提供了更为灵活和强大的解决方案。

Redux-saga 是一个基于 Generator 的库,允许我们使用类似同步代码的方式编写异步代码,并且允许我们在 Redux 应用程序中进行复杂的控制流程和较为轻松的测试。

实现原理

Redux-saga 允许我们使用 Generator 函数来编写 saga。saga 是一段代码,在 saga 中会监听特定的 action,一旦符合则会触发一些异步操作。

--------- --------------------- -
  --- -
    ----- ---- - ----- ------------------- -----------------------
    ----- ----- ----- --------------------- -------- - ---- - ---
  - ----- ------- -
    ----- ----- ----- ------------------- -------- - ----- - ---
  -
-

--------- -------- -
  ----- ----------------------- ---------------
-

在上面的代码中,我们定义了一个名为 fetchUserSaga 的 Generator 函数,用于处理 action 类型为 FETCH_USER 的异步操作。在 saga 中,我们使用 call() 方法来调用 api.fetchUser() 方法,该方法返回 Promise。如果 Promise 成功执行,则使用 put() 方法来分发 FETCH_USER_SUCCESS。

在 mySaga 中,我们使用 takeEvery() 方法来监听 FETCH_USER action,当收到匹配的 DialogflowAction 后使用 fork 来执行相应的 saga。

案例分析

假设我们有一个 gallery 应用,用户可以在应用中浏览相片。当用户选择相片时,应用会自动开始加载相应的数据。我们可以使用 Redux-saga 来处理这个过程,并在完成后将数据作为 payload 分发给 reducer。

--------- ------------------------ -
  --- -
    ----- ----- ----- ---------------------- ---
    ----- ----------- - ----- -------------------------- --------------------------
    ----- ----- ----- ----------------------------- -------- ----------- ---
  - ----- ------- -
    ----- ----- ----- --------------------------- -------- ----- ---
  -
-

--------- ----------------------- -
  ----- ------------------------------- ------------------
-

------ ------- --------- ---------- -
  ----- -----
    ------------------------
  ---
-

在上面的代码中,我们定义了一个 Generator 方法 fetchGalleryData 用于处理 FETCH_GALLERY_DATA action,当收到匹配的 action 时,我们使用 try/catch 代码块包裹整个异步操作代码。在代码块内部,我们使用 put() 方法来分发 LOADING_GALLERY_DATA action,告诉应用已开始加载数据,同时使用 call() 方法来等待 api.fetchGalleryData() 方法返回数据。

当 api.fetchGalleryData() 执行后,我们使用 put() 方法来分发 FETCH_GALLERY_DATA_SUCCESS action,并将 fetchGalleryData 的结果作为 payload 传递给 action。如果异常发生,则触发 FETCH_GALLERY_DATA_ERROR action,并提供错误信息。

与 Redux-thunk 和 Redux-promise 不同,Redux-saga 允许我们在应用程序中执行真正的异步操作。这个库不仅提供了灵活和强大的解决方案,而且可以更容易地进行测试和重构。

结论

在本文中,我们通过讨论 Redux-thunk、Redux-promise 和 Redux-saga,深入了解了使用中间件处理异步操作的方法。同时,我们也了解了这些中间件在 Redux 源码中的实现原理。

在实际开发中,我们可以根据业务场景选择不同的中间件,对于较为简单的异步操作,我们可以使用 Redux-thunk 或 Redux-promise,对于较为复杂的异步操作,我们可以使用 Redux-saga 来处理相关逻辑。在使用中间件时,我们也需要谨慎思考,确保异步操作的状态正确,以避免出现困难和错误。

来源:JavaScript中文网 ,转载请注明来源 本文地址:https://www.javascriptcn.com/post/670f77c65f5512810264abe8