乐闻世界logo
搜索文章和话题

When to use fork in redux saga

7 个月前提问
3 个月前修改
浏览次数25

3个答案

1
2
3

forkredux-saga 是一种非阻塞调用效果,用于创建一个新的 saga 分支,该分支可以同时与父 saga 运行。使用 fork 的场合通常包括以下几点:

  1. 并发执行任务:当你希望启动一个新的任务而不阻塞当前的流程时,可以使用 fork。这允许同时执行多个任务。

    例子:假设在一个用户登录的流程中,我需要并行地从多个源获取数据,比如用户信息、用户设置以及用户消息。我可以通过 fork 来分别启动三个不同的 saga,它们将并行执行,而不会互相等待。

    javascript
    function* loginFlow() { // ... 登录逻辑 yield fork(fetchUserInfo); yield fork(fetchUserSettings); yield fork(fetchUserMessages); // ... 其他逻辑 }
  2. 非关键任务:如果有一些任务是次要的,或者说它的完成与否不会影响当前主流程的继续,可以使用 fork 来执行。

    例子:在提交表单数据后,我可能想要记录一些统计数据,但是不希望统计代码的失败影响主流程。

    javascript
    function* submitFormSaga(data) { try { yield call(api.submitForm, data); // 主要任务 yield put({ type: 'FORM_SUBMIT_SUCCESS' }); yield fork(recordFormSubmitStats, data); // 非关键任务 } catch (e) { yield put({ type: 'FORM_SUBMIT_FAILURE', message: e.message }); } }
  3. 长期运行的监听器fork 可用于启动一个任务,该任务将长期运行并监听将来可能发生的动作。它作为一个后台任务,在后台持续监听某些动作而不阻塞其他 saga

    例子:一个聊天应用可能需要一个 saga 来监听接收新消息的动作。

    javascript
    function* watchNewMessages() { while (true) { const action = yield take('RECEIVE_MESSAGE'); // 处理接收到的消息 } } function* mainSaga() { // ... yield fork(watchNewMessages); // ... }

在使用 fork 时,需要注意的是,fork 创建的任务是不会阻塞父 saga 的继续执行。如果你需要确保任务完成后再继续,应该使用 call 效果。此外,fork 创建的任务在出错时不会传播错误到父 saga,这意味着如果不处理,可能会导致在后台默默地失败。因此,启动 fork 任务时通常需要在任务中进行适当的错误处理。

2024年6月29日 12:07 回复

一般来说,fork当传奇需要启动非阻塞任务时,这是有用的。这里的非阻塞是指:调用者启动任务并继续执行,而不等待它完成。

在多种情况下,此功能都非常有用,但主要有两种:

  • 按逻辑域对 sagas 进行分组
  • 保留对任务的引用以便能够取消/加入它

您的顶级传奇可以是第一个用例的示例。你可能会遇到类似的情况:

shell
yield fork(authSaga); yield fork(myDomainSpecificSaga); // you could use here something like yield []; // but it wouldn't make any difference here

其中authSaga可能包括以下内容:

shell
yield takeEvery(USER_REQUESTED_LOGIN, authenticateUser); yield takeEvery(USER_REQUESTED_LOGOUT, logoutUser);

您可以看到这个示例与您所建议的等效,即使用forksaga 进行调用并产生takeEvery调用。但实际上,您只需出于代码组织目的而执行此操作。takeEvery本身就是一个分叉任务,因此在大多数情况下,这将是无用的冗余。

第二个用例的示例如下:

shell
yield take(USER_WAS_AUTHENTICATED); const task = yield fork(monitorUserProfileUpdates); yield take(USER_SIGNED_OUT); yield cancel(task);

您可以在这个示例中看到,将monitorUserProfileUpdates在调用者传奇恢复时执行,并等待USER_SIGNED_OUT要分派的操作。它还可以保留对其的引用,以便在需要时取消它。

为了完整起见,还有另一种方式来启动非阻塞调用:spawnfork不同之spawn处在于错误和取消从子传奇到父传奇的方式。

2024年6月29日 12:07 回复

通常fork对于某些具有多次 API 调用分派的情况变得更有用,原因是您可以通过实例化任务中的取消来拒绝这些获取,例如cancel(task1);

如果最终用户强制退出应用程序,或者其中一项任务失败,导致您的指令、策略和逻辑出现问题,并且取消或终止 saga 上的当前处理任务可能是合理的,则非常有用;

2种方法可以取消任务

来自redux-saga 非阻塞效应取消文档的基础

shell
import { take, put, call, fork, cancel } from 'redux-saga/effects' // ... function* loginFlow() { while (true) { const {user, password} = yield take('LOGIN_REQUEST') // Non-Blocking Effect which is the fork const task = yield fork(authorize, user, password) const action = yield take(['LOGOUT', 'LOGIN_ERROR']) if (action.type === 'LOGOUT'){ //cancel the task yield cancel(task) yield call(Api.clearItem, 'token') } } }

或者

shell
import {call, put, fork, delay} from 'redux-saga/effects'; import someAction from 'action/someAction'; function* fetchAll() { yield fork(fetcher, 'users'); yield fork(fetcher, 'posts'); yield fork(fetcher, 'comments'); yield delay(1500); } function* fetcher(endpoint) { const res = yield call(fetchAPI, endpoint); if (!res.status) { throw new Error(`Error: ${res.error}`); } yield put(someAction({payload: res.payload})); } function* worker() { try { yield call(fetchAll); } catch (err) { // handle fetchAll errors } } function* watcher() { yield takeEvery(BLOGS.PUSH, worker); }

别客气 :)

2024年6月29日 12:07 回复

你的答案