如何在 redux-saga 中等待 saga 的执行完成?

Posted

技术标签:

【中文标题】如何在 redux-saga 中等待 saga 的执行完成?【英文标题】:How to wait execution of a saga to finish in redux-saga? 【发布时间】:2019-03-18 03:28:19 【问题描述】:

我有以下场景:

export function* addCircle(circleApi,  payload ) 
    try 
        const response = yield apply(
            circleApi,
            circleApi.addCircle,
            [payload]
        );

        if (response.error_type) 
           yield put(addCircleFailedAction(response.error));
         else 
            yield put(addCircleSucceededAction(response));
        
     catch (err) 
        console.error(err);
    


export function* addTender(tenderApi,  payload ) 
    try 
       // NOTE: I want this to finish before continuing with rest of saga below.
       yield call(addCircleAction(payload.circlePayload));

       // Rest of saga removed for brevity.
     catch (err) 
        console.error(err);
    

所以,基本上addCircle 正在进行 API 调用,并且根据其成功,我调用适当的 redux 操作。现在,在另一个 saga 中,我调用了负责 addCircle saga 的操作,我希望它在继续执行 saga 的其余部分之前完成执行。我尝试使用 call,但它基本上不会等待 addCircle saga 完成执行。有什么方法可以等待吗?我从我的组件内部调用addCircle,我不需要等待它,但在这个特定的例子中,我必须在 saga 内部调用它,所以我真的需要等待它完成执行,改变状态的应用程序,这样我就可以在 addTender saga 的其余部分中使用更新的状态。有什么想法吗?

【问题讨论】:

看看下面使用while (someCondition) redux-saga.js.org/docs/advanced/…的例子 @AlexanderStaroselsky 我不太清楚在我的情况下我该怎么做。你有什么想法?因为所有相关的动作和 sagas 都已经在代码中看到了。 【参考方案1】:

如果您正在调度多个会触发 addRender 的操作,则无法保证 take(...) 会真正等待由 yield 调用产生的操作。

export function* addCircle(circleApi,  payload ) 
  try 
      const response = yield apply(
          circleApi,
          circleApi.addCircle,
          [payload]
      );

      if (response.error_type) 
         yield put(addCircleFailedAction(response.error));
         return response;
       else 
          yield put(addCircleSucceededAction(response));
          return response;
      
   catch (err) 
      console.error(err);
      return err;
  


export function* addTender(tenderApi,  payload ) 
  try 
     //because addCircle saga is returning something you can re use it
     // in other sagas.
     const result = yield call(addCircle,circleAPI?,payload.circlePayload);
     //check for result.error_type here
     // Rest of saga removed for brevity.
   catch (err) 
      console.error(err);
  

您的代码和接受的答案将导致错误,因为call 没有将操作对象作为第一个参数(它确实采用 context,fn 类型的对象)。

分派一个动作,然后监听另一个可能是或可能不是您刚刚分派的动作的副作用的动作是糟糕的设计。您异步调度这些操作,并且无法保证它们都需要相同的时间来完成或提供您等待的副作用,它们的启动顺序与它们的启动顺序相同​​。

【讨论】:

这并没有给出实际的答案。根据 redux-saga 的说法, apply 只是一个具有不同 signature 的别名。考虑到有关返回值的 cmets,使用 apply 似乎也不是您要暗示的答案。你的答案是调用一个使用返回值为你执行解析逻辑的传奇吗?【参考方案2】:

根据您的代码 sn-p,您的 addCircle 传奇将在完成执行之前调度 addCircleFailedActionaddCircleSucceededAction 动作创建者。因此,我们将不得不在您的 addTender 传奇中等待这些操作。

基本上,这是您应该做的。我只是根据动作创建者的名字猜测你的动作类型。

yield call(addCircleAction(payload.circlePayload));
yield take([ADD_CIRCLE_FAILED_ACTION, ADD_CIRCLE_SUCCEEDED_ACTION]);

// Rest of the saga

虽然有一种极端情况。您没有在 addCircle 传奇的 catch 块中调度任何操作。也许您可以在 catch 块内调度一个名为 addCircleExceptionAction 的操作,并与其他操作一起等待它,如下所示:

yield take([ADD_CIRCLE_FAILED_ACTION, ADD_CIRCLE_SUCCEEDED_ACTION, ADD_CIRCLE_EXCEPTION_ACTION]);

【讨论】:

@terett 我不建议使用它。假设yield call 实际上是yield put,因为yield call(generatorFunction,args) 会等待函数完成。现在您调度一个动作并等待该动作的结果或任何先前/以后调度的相同类型的动作的结果。 @terret 实际上@HMR 有好处。我也错过了yield call(addCircleAction... 应该是正确的call 效果。你真的想出于某种原因发送addCircleAction 吗?如果没有,@HMR 的回答最合适。 @terett 我猜你需要 3 个动作:CIRCLE_NEEDED 由 UI 调度。 CIRCLE_REQUESTEDCIRLE_CREATED 由 addCircle saga 调度(并返回结果,因此 addCircle 可以被其他 saga 调用)。现在 reducer 可以设置加载和结果,同时仍然可以选择直接从其他 saga 调用 addCirlce(无需调度操作)。

以上是关于如何在 redux-saga 中等待 saga 的执行完成?的主要内容,如果未能解决你的问题,请参考以下文章

如何让 ES6 生成器等待承诺,就像在 redux-saga 中一样?

无法弄清楚如何使用 redux-saga-test-plan 测试 redux-saga 功能

redux-saga学习进阶篇二

redux-saga 的单元测试抛出错误:必须在迭代器上调用 runSaga

反应如何在 redux-saga 中导航路由器?

如何使用 redux-saga 在 API 响应中检查过期的 JWT 令牌?