有没有承诺,任何类似的事情在redux Saga ?

rbl8hiat  于 2022-11-12  发布在  其他
关注(0)|答案(1)|浏览(143)

我知道Redux Saga 的all([...effects])效果组合器非常类似于Promise.all实用程序,但我还没有找到类似于Promise.any行为的东西:

  • 同时运行所有效果
  • 如果所有效果都失败,则失败(否则成功)
  • 如果失败,则抛出所有错误AggregateError
  • 如果成功,则不返回任何结果或仅返回第一个结果(来自多个结果)

例如:

export function* getHomeDataSaga() {
  yield* any([
    call(getTopUsersSaga, { payload: undefined }),
    call(getFavoritesSaga, { payload: undefined }),
    call(getTrendingTokensSaga, { payload: undefined }),
    call(getTopCollectionsSaga, { payload: { itemsPerPage: 9, page: 1 } }),
  ]);
}

当你想把多个(分解的)传奇组合成一个传奇时,这将是非常有用的,它不会快速失败,但会完成所有的效果。

答案

基于Martin Kadlec的答案最终使用:

export function* anyCombinator(effects: SagaGenerator<any, any>[]) {
  const errors = yield* all(
    effects.map((effect) =>
      call(function* () {
        try {
          yield* effect;
          return null;
        } catch (error) {
          return error;
        }
      }),
    ),
  );

  if (errors.every((error) => error !== null)) {
    throw new AggregateError(errors);
  }
}
ivqmmu1c

ivqmmu1c1#

目前还没有这样的效果,但是您可以创建自己的实用程序来为您执行这些操作。any功能与all功能非常相似,在一种情况下,您将获得所有的结果/错误,而在另一种情况下,您将获得第一个成功/失败的结果。因此,您可以轻松地获得any功能,方法是为成功时抛出的每个项目翻转all效果-〉,并在出错时返回。

const sagaAny = (effects = []) => {
  const taskRunner = function* (effect) {
    let value;
    try {
      value = yield effect;
    } catch (err) {
      // On error, we want to just return it 
      // to map it later to AggregateError
      return err;
    }
    // On success we want to cancel all the runners
    // we do that by throwing here
    throw value;
  };

  return call(function* () {
    try {
      const runners = effects.map((effect) => call(taskRunner, effect));
      // If one of the runners throws on success the all effect will
      // cancel all the other runners
      const failedResults = yield all(runners);
      throw new AggregateError(failedResults, "SAGA_ANY");
    } catch (err) {
      if (err instanceof AggregateError) throw err;
      return err;
    }
  });
};

function* getHomeDataSaga() {
  const result = yield sagaAny([
    call(getTopUsersSaga, { payload: undefined }),
    call(getFavoritesSaga, { payload: undefined }),
    call(getTrendingTokensSaga, { payload: undefined }),
    call(getTopCollectionsSaga, { payload: { itemsPerPage: 9, page: 1 } }),
  ]);
}

如果你不想取消其他传奇一旦一个成功,事情变得有点棘手,因为在标准的叉树主要传奇(例如getHomeDataSaga)会等到所有分叉的saga(任务运行者)完成后再继续。为了绕过这个问题,我们可以使用spawn效应,这将不会阻止主 Saga ,虽然它有一些其他的含义(eidogg.如果你杀死他们的主传奇,衍生的传奇将继续运行)。
类似这样的东西应该可以做到这一点:

const sagaAny = (effects = []) => {
  const taskRunner = function* (effect, resultsChannel) {
    try {
      value = yield effect;
      yield put(resultsChannel, { type: "success", value });
    } catch (err) {
      yield put(resultsChannel, { type: "error", value: err });
    }
  };

  return call(function* () {
    const resultsChannel = yield call(channel);
    yield all(
      effects.map((effect) => spawn(taskRunner, effect, resultsChannel))
    );
    const errors = [];
    while (errors.length < effects.length) {
      const result = yield take(resultsChannel);
      if (result.type === "success") {
        yield put(resultsChannel, END);
        return result.value;
      }
      if (result.type === "error") errors.push(result.value);
    }
    throw new AggregateError(errors, "SAGA_ANY");
  });
};

我在这里使用自定义通道将生成的跑步者的结果发送到实用程序 Saga ,这样我就可以根据我的需要对每个完成的跑步者做出React。

相关问题