'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 中抛出错误会变得混乱。例如,您不能直接捕获 forked 任务抛出的错误。此外,使用动作来表示 saga 结果会在您的商店中保留一个良好的事件日志,其他 sagas/reducers 可以响应。当您 call 其他传奇时,应该启动该传奇的操作(例如 takeEvery(THE_ACTION, ...))不会被调度。