'yield all' 在 Saga 中不等待所有效果完成
'yield all' in Saga is not waiting for all the effects to complete
我有一个这样的 Saga(一些伪代码)。
Saga1 调用 API。根据结果,我需要再调用两个 API。如果所有 API 都成功,我调用 onSuccess,否则调用 onFailure。
代码似乎几乎可以正常工作,但不完全是。我面对 yield all
的问题是,一旦调用第一个 yield put,它就认为 saga2 和 saga3 已完成(请参阅 saga2/3 中的评论)。它没有等待获取收益完成。
我认为部分原因是我对 "complete effect" 的含义有误解。但除此之外,我希望 yield all 等到一切都完成。我希望 saga2/3 中的 fetch 抛出的任何异常都被 saga1 中的 catch 捕获。
saga1(action) {
const { onSuccess, onFailure } = action.payload;
try {
yield fetch...
if(response.some_condition) yield all([
put(saga2()),
put(saga3())
])
onSuccess();
}
catch(e) {
onFailure();
}
}
saga2(action) {
yield put(someaction()) // This yields
yield fetch...
}
saga3(action) {
yield put(someaction()) // This yield
yield fetch...
}
下面这段代码与我在下面关于 catch 不工作的评论有关
action1 () { // action2 is same
try {
yield fetch();
yield put(FINISHED_1);
}
catch(e) {
throw (e);
}
}
saga1() {
try {
yield put(action1());
yield put(action2());
yield all([
take(FINISHED_1),
take(FINISHED_2),
])
console.log("this doesn't print if exception in either action");
}
catch(e) {
console.log("this doesn't print if exception in either action");
}
finally {
console.log("this prints fine");
}
}
1) 等待多个 call
效果 运行 完成:
yield all([
call(saga2, arg1, arg2, ...),
call(saga3, arg1, arg2, ...)
]);
2) 调度多个动作并等待它们的成功动作被调度:
yield put(action1());
yield put(action2());
yield all([
take(ACTION_1_SUCCESS),
take(ACTION_2_SUCCESS)
]);
编辑回复评论
如果你直接用all
调用sagas(上面的#1),那么你可以按常规捕获错误
try {
yield all([
call(saga2, arg1, arg2, ...),
call(saga3, arg1, arg2, ...)
]);
} catch (e) {
// ...
}
但是,如果一个 saga put
的动作被其他 sagas 侦听,则该 saga 不会收到这些异常。 Saga1
不是这些传奇的父级或附属于这些传奇。它只是调度动作,其他地方的一些其他任务会监听和响应。
为了 Saga1
了解那些 sagas 中的错误,sagas 不应该抛出错误,而是派发一个带有错误负载的动作:
function* saga2(action) {
try {
const result = yield call(...);
yield put(action2Success(result));
} catch (e) {
yield put(action2Failure(e.message));
}
}
触发saga2
(通过put(action2())
)的传奇可以处理成功和失败:
function* saga1(action) {
yield put(action2());
yield put(action3());
const [success, failure] = yield race([
// if this occurs first, the race will exit, and success will be truthy
all([
take(ACTION_2_SUCCESS),
take(ACTION_3_SUCCESS)
]),
// if either of these occurs first, the race will exit, and failure will be truthy
take(ACTION_2_FAILURE),
take(ACTION_3_FAILURE)
]);
if (failure) {
return;
}
// ...
}
Sagas 应该处理异常并使用错误状态更新存储,而不是抛出错误。使用 saga 并发结构时,在 sagas 中抛出错误会变得混乱。例如,您不能直接捕获 fork
ed 任务抛出的错误。此外,使用动作来表示 saga 结果会在您的商店中保留一个良好的事件日志,其他 sagas/reducers 可以响应。当您 call
其他传奇时,应该启动该传奇的操作(例如 takeEvery(THE_ACTION, ...)
)不会被调度。
我有一个这样的 Saga(一些伪代码)。
Saga1 调用 API。根据结果,我需要再调用两个 API。如果所有 API 都成功,我调用 onSuccess,否则调用 onFailure。
代码似乎几乎可以正常工作,但不完全是。我面对 yield all
的问题是,一旦调用第一个 yield put,它就认为 saga2 和 saga3 已完成(请参阅 saga2/3 中的评论)。它没有等待获取收益完成。
我认为部分原因是我对 "complete effect" 的含义有误解。但除此之外,我希望 yield all 等到一切都完成。我希望 saga2/3 中的 fetch 抛出的任何异常都被 saga1 中的 catch 捕获。
saga1(action) {
const { onSuccess, onFailure } = action.payload;
try {
yield fetch...
if(response.some_condition) yield all([
put(saga2()),
put(saga3())
])
onSuccess();
}
catch(e) {
onFailure();
}
}
saga2(action) {
yield put(someaction()) // This yields
yield fetch...
}
saga3(action) {
yield put(someaction()) // This yield
yield fetch...
}
下面这段代码与我在下面关于 catch 不工作的评论有关
action1 () { // action2 is same
try {
yield fetch();
yield put(FINISHED_1);
}
catch(e) {
throw (e);
}
}
saga1() {
try {
yield put(action1());
yield put(action2());
yield all([
take(FINISHED_1),
take(FINISHED_2),
])
console.log("this doesn't print if exception in either action");
}
catch(e) {
console.log("this doesn't print if exception in either action");
}
finally {
console.log("this prints fine");
}
}
1) 等待多个 call
效果 运行 完成:
yield all([
call(saga2, arg1, arg2, ...),
call(saga3, arg1, arg2, ...)
]);
2) 调度多个动作并等待它们的成功动作被调度:
yield put(action1());
yield put(action2());
yield all([
take(ACTION_1_SUCCESS),
take(ACTION_2_SUCCESS)
]);
编辑回复评论
如果你直接用all
调用sagas(上面的#1),那么你可以按常规捕获错误
try {
yield all([
call(saga2, arg1, arg2, ...),
call(saga3, arg1, arg2, ...)
]);
} catch (e) {
// ...
}
但是,如果一个 saga put
的动作被其他 sagas 侦听,则该 saga 不会收到这些异常。 Saga1
不是这些传奇的父级或附属于这些传奇。它只是调度动作,其他地方的一些其他任务会监听和响应。
为了 Saga1
了解那些 sagas 中的错误,sagas 不应该抛出错误,而是派发一个带有错误负载的动作:
function* saga2(action) {
try {
const result = yield call(...);
yield put(action2Success(result));
} catch (e) {
yield put(action2Failure(e.message));
}
}
触发saga2
(通过put(action2())
)的传奇可以处理成功和失败:
function* saga1(action) {
yield put(action2());
yield put(action3());
const [success, failure] = yield race([
// if this occurs first, the race will exit, and success will be truthy
all([
take(ACTION_2_SUCCESS),
take(ACTION_3_SUCCESS)
]),
// if either of these occurs first, the race will exit, and failure will be truthy
take(ACTION_2_FAILURE),
take(ACTION_3_FAILURE)
]);
if (failure) {
return;
}
// ...
}
Sagas 应该处理异常并使用错误状态更新存储,而不是抛出错误。使用 saga 并发结构时,在 sagas 中抛出错误会变得混乱。例如,您不能直接捕获 fork
ed 任务抛出的错误。此外,使用动作来表示 saga 结果会在您的商店中保留一个良好的事件日志,其他 sagas/reducers 可以响应。当您 call
其他传奇时,应该启动该传奇的操作(例如 takeEvery(THE_ACTION, ...)
)不会被调度。