【问题标题】:'yield all' in Saga is not waiting for all the effects to completeSaga 中的“yield all”并非等待所有效果完成
【发布时间】:2018-10-30 19:30:31
【问题描述】:

我有一个像这样的传奇(一些伪代码)。

Saga1 调用一个 API。根据结果​​,我需要调用另外两个 API。如果所有 API 都成功,我调用 onSuccess,否则调用 onFailure。

代码似乎几乎可以正常工作,但并不完全正常。 yield all 面临的问题是,一旦调用了第一个 yield put,它就认为 saga2 和 saga3 完成了(参见 saga2/3 中的评论)。它没有等待获取收益完成。

我认为部分原因是我误解了“完全效果”的含义。但除此之外,我希望让所有东西都等到一切都完成。我希望 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 not working 的评论有关

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");
    }
}

【问题讨论】:

  • 您的示例代码似乎有点不对劲。您将 saga 传递给 put,这是不正确的。您可以将操作传递给put,例如put({ type: 'ACTION_2' }),然后saga2 通过takeEvery('ACTION_2', saga2) 侦听该操作。或者您可以将 saga 传递给 call,例如 call(saga2, arg1, arg2)
  • @bsapaka 为了简单起见,我只是简化了代码。我实际上是在调用 put({type:ACTION2}) put({type:ACTION3}),而后者又调用了 saga2 或 3。
  • 您可以使用yield take(ACTION_3_FINISHED) 等待ACTION_3_FINISHED 被调度

标签: reactjs redux redux-saga


【解决方案1】:

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)
]);

响应 cmets 的编辑

如果您直接使用 all(上面的#1)调用 sagas,那么您可以按照惯例捕获错误

try {
    yield all([
        call(saga2, arg1, arg2, ...),
        call(saga3, arg1, arg2, ...)
    ]);
} catch (e) {
    // ...
}

但是,如果一个 saga puts 动作被其他 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()))的saga 可以处理成功和失败:

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

【讨论】:

  • 天哪,这太理想了。正是我所要求的。这对我来说并没有解决一个问题(这是我最初想做的,但从未问过,因为我认为这会解决它)。在行动 1 的传奇中,我进行了 API 调用,并在 catch 中重新抛出了错误。由于 yield all 应该向我抛出这些错误,因此 yield all 周围的 try/catch 应该捕获抛出的错误。但事实并非如此。但它也不会在 yield all 之后计算代码,这意味着它肯定会看到一个错误,这就是它退出的原因。但它并没有被抓住。我的终于还在运行。更多在原始评论中
  • 顺便说一句,我仍然选择您的评论作为接受的答案,因为这完全回答了我最初的问题。我只是认为 catchy 会起作用,但猜不到。如果你能在原始评论中查看我的编辑,那就太棒了!
  • 你能检查一下jsbin.com/huwobicege/edit?js 这解释了我为什么使用catch。所有非 200 响应都会被捕获。它工作得很好而且花花公子,但如果我从另一个传奇中调用一个传奇就不行了。我想我可以以不使用 catch 的方式对其进行重构,但对于单个 API 调用来说,这似乎是最好的方法。我只是对抛出错误感到困惑,这迫使调用 try 块中途停止(因此它看到错误,并且不执行 try 块的其余部分),调用 finally 部分,但不捕获。跨度>
  • 我刚刚重新阅读了您的回复。让我试试看。非常感谢。
  • 我添加了一些东西来澄清
猜你喜欢
  • 2020-10-04
  • 1970-01-01
  • 1970-01-01
  • 2019-05-13
  • 1970-01-01
  • 2022-08-22
  • 2018-06-03
  • 1970-01-01
  • 2019-03-18
相关资源
最近更新 更多