具有多个 API 调用的 Redux saga 错误处理
Redux saga error handling with multiple API calls
我有两个 thunk,我正在尝试将其转换为一个 saga。当用户登录到他们的仪表板时,他们会进行两次 API 调用,一次用于项目,另一次用于通知。在 thunk 中,遵循 Flux 标准操作调用每个错误是微不足道的,但我不确定用 sagas 执行此操作的最佳方法是什么。这就是我要开始的:
export function* loadDashboard() {
try {
yield put({ type: types.notificationTypes.GET_NOTIFICATIONS_REQUEST });
const [projects, notifications] = yield all([
DashboardService.getProjects,
DashboardService.getNotifications
]);
yield put({
type: types.projectTypes.GET_PROJECTS_SUCCESS,
payload: projects
});
yield put({
type: types.notificationTypes.GET_NOTIFICATIONS_SUCCESS,
payload: notifications
});
} catch (error) {
//My guess is both of these will render the same error to their reducer,
//regardless of who the originator is
yield put({
type: types.projectTypes.GET_PROJECTS_FAILURE,
error: error.toString()
});
yield put({
type: types.notificationTypes.GET_NOTIFICATIONS_FAILURE,
error: error.toString()
});
}
}
我认为最好修复两个API请求的逻辑。
这提高了可读性和可维护性:
export function* loadDashboard() {
yield all([loadProjects, loadNotifications])
}
function* loadProjects() {
try {
yield put({ type: types.GET_PROJECTS_REQUEST })
const projects = yield call(DashboardService.getProjects)
yield put({
type: types.projectTypes.GET_PROJECTS_SUCCESS,
payload: projects
})
} catch (error) {
yield put({
type: types.projectTypes.GET_PROJECTS_FAILURE,
error: error.toString()
})
}
}
function* loadNotifications() {
try {
yield put({ type: types.notificationTypes.GET_NOTIFICATIONS_REQUEST });
const notifications = yield call(DashboardService.getNotifications)
yield put({
type: types.notificationTypes.GET_NOTIFICATIONS_SUCCESS,
payload: notifications
})
} catch (error) {
yield put({
type: types.notificationTypes.GET_NOTIFICATIONS_FAILURE,
error: error.toString()
})
}
}
不过这段代码有点不同。
Redux-saga 的 all()
is actually all-or-nothing:如果其中一个任务抛出错误,所有仍然 运行 的任务都会被取消。
我不认为这是你想要的,所以在我的代码中我通过捕获每个请求的错误来防止这种情况。
Task cancellation 和取消传播是 sagas 与 promises 的主要区别。这些东西通常默认工作,所以花点时间了解它们。
我最终将 all()
与 spawn()
合并:
export function* loadDashboard() {
yield all([
spawn(projectActions.getProjects),
spawn(notificationActions.getNotifications),
spawn(teamActions.getTeams)
]);
}
"分离叉(使用 spawn)
分离的分支存在于它们自己的执行上下文中。 parent 不会等待分离的叉子终止。来自生成任务的未捕获错误不会冒泡到 parent。并且取消 parent 不会自动取消分离的分叉(您需要明确取消它们)。
简而言之,分离分支的行为就像直接使用 middleware.run API 开始的根 Sagas。"
我通过从总共 3 个请求中的一个 GET
请求中排除 auth headers 来手动测试这个,并且错误单独传播,而其他人继续成功。我 运行 测试 SUCCESS 调用是否在失败后到达,只是为了确定。但是,当用 3 个 fork()
而不是 3 个 spawn()
调用编写相同的 loadDashboard()
方法时,该行为在功能上似乎是等效的。将需要编写适当的测试来确定哪种实现最适合这种情况。
编辑:在过去几个月 using/implementing saga 组合之后,我相信当你希望能够 cancel()
child 进程和 spawn
是您不关心任务取消的时候。
我有两个 thunk,我正在尝试将其转换为一个 saga。当用户登录到他们的仪表板时,他们会进行两次 API 调用,一次用于项目,另一次用于通知。在 thunk 中,遵循 Flux 标准操作调用每个错误是微不足道的,但我不确定用 sagas 执行此操作的最佳方法是什么。这就是我要开始的:
export function* loadDashboard() {
try {
yield put({ type: types.notificationTypes.GET_NOTIFICATIONS_REQUEST });
const [projects, notifications] = yield all([
DashboardService.getProjects,
DashboardService.getNotifications
]);
yield put({
type: types.projectTypes.GET_PROJECTS_SUCCESS,
payload: projects
});
yield put({
type: types.notificationTypes.GET_NOTIFICATIONS_SUCCESS,
payload: notifications
});
} catch (error) {
//My guess is both of these will render the same error to their reducer,
//regardless of who the originator is
yield put({
type: types.projectTypes.GET_PROJECTS_FAILURE,
error: error.toString()
});
yield put({
type: types.notificationTypes.GET_NOTIFICATIONS_FAILURE,
error: error.toString()
});
}
}
我认为最好修复两个API请求的逻辑。
这提高了可读性和可维护性:
export function* loadDashboard() {
yield all([loadProjects, loadNotifications])
}
function* loadProjects() {
try {
yield put({ type: types.GET_PROJECTS_REQUEST })
const projects = yield call(DashboardService.getProjects)
yield put({
type: types.projectTypes.GET_PROJECTS_SUCCESS,
payload: projects
})
} catch (error) {
yield put({
type: types.projectTypes.GET_PROJECTS_FAILURE,
error: error.toString()
})
}
}
function* loadNotifications() {
try {
yield put({ type: types.notificationTypes.GET_NOTIFICATIONS_REQUEST });
const notifications = yield call(DashboardService.getNotifications)
yield put({
type: types.notificationTypes.GET_NOTIFICATIONS_SUCCESS,
payload: notifications
})
} catch (error) {
yield put({
type: types.notificationTypes.GET_NOTIFICATIONS_FAILURE,
error: error.toString()
})
}
}
不过这段代码有点不同。
Redux-saga 的 all()
is actually all-or-nothing:如果其中一个任务抛出错误,所有仍然 运行 的任务都会被取消。
我不认为这是你想要的,所以在我的代码中我通过捕获每个请求的错误来防止这种情况。
Task cancellation 和取消传播是 sagas 与 promises 的主要区别。这些东西通常默认工作,所以花点时间了解它们。
我最终将 all()
与 spawn()
合并:
export function* loadDashboard() {
yield all([
spawn(projectActions.getProjects),
spawn(notificationActions.getNotifications),
spawn(teamActions.getTeams)
]);
}
"分离叉(使用 spawn) 分离的分支存在于它们自己的执行上下文中。 parent 不会等待分离的叉子终止。来自生成任务的未捕获错误不会冒泡到 parent。并且取消 parent 不会自动取消分离的分叉(您需要明确取消它们)。
简而言之,分离分支的行为就像直接使用 middleware.run API 开始的根 Sagas。"
我通过从总共 3 个请求中的一个 GET
请求中排除 auth headers 来手动测试这个,并且错误单独传播,而其他人继续成功。我 运行 测试 SUCCESS 调用是否在失败后到达,只是为了确定。但是,当用 3 个 fork()
而不是 3 个 spawn()
调用编写相同的 loadDashboard()
方法时,该行为在功能上似乎是等效的。将需要编写适当的测试来确定哪种实现最适合这种情况。
编辑:在过去几个月 using/implementing saga 组合之后,我相信当你希望能够 cancel()
child 进程和 spawn
是您不关心任务取消的时候。