39

redux-saga 项目已经存在了很长时间,但是这个库仍然有很多令人困惑的地方。其中之一是:如何启动你的 rootSaga。例如,在初学者教程中, rootSaga 是通过生成一个 saga 数组来启动的。像这样

export default function* rootSaga() {
  yield [
    helloSaga(),
    watchIncrementAsync()
  ]
}

然而,在using saga helpers部分,rootSaga 由两个分叉的 saga 组成。像这样:

export default function* rootSaga() {
  yield fork(watchFetchUsers)
  yield fork(watchCreateUser)
}

在 redux-saga repo 的异步示例中使用了相同的启动 rootSaga 的方式。但是,如果您查看真实世界和购物卡示例,您会看到那里的 rootSagas 产生了一系列分叉的 saga。像这样:

export default function* root() {
  yield [
    fork(getAllProducts),
    fork(watchGetProducts),
    fork(watchCheckout)
  ]
}

此外,如果您阅读有关 redux-saga 问题的一些讨论,您会看到有些人建议使用 spawn 而不是 fork 用于 rootSaga 以防止您的应用程序在您的一个分叉 saga 因某些未处理的异常而被取消时完全崩溃。

那么,哪种方式是启动 rootSaga 的最正确方式呢?现有的有什么区别?

4

2 回答 2

17

您可以启动多个根 sagas。但是任何传奇都有能力自己开始另一个传奇。因此,可以启动单个根 saga,从而创建其他 saga。

您只需要了解错误如何传播到父 saga。如果你有一个根 saga 并且一个子 saga 崩溃,默认情况下,错误将传播到将终止的父级,这也将终止从该父级启动的所有其他 saga。

由您决定此行为。根据您的应用程序,您可能希望有一个快速失败的行为(如果出现此类问题,使整个应用程序无法使用),或者故障安全,并尝试使应用程序继续工作,即使某些部分可能有问题。

一般来说,我建议您启动多个根 saga,或者您的父 saga 使用spawn而不是fork这样,以便您的应用在发生崩溃时仍然可用。请注意,在某些地方也很容易忘记捕获错误。例如,如果有一个 API 请求失败,您通常不希望您的所有应用程序都无法使用

编辑:我建议看看https://github.com/yelouafi/redux-saga/issues/570

在这个redux-saga 问题中,我展示了启动 saga 的不同方法以及它对您的应用程序的影响。

TLDR:这就是我通常开始 root sagas 的方式:

const makeRestartable = (saga) => {
  return function* () {
    yield spawn(function* () {
      while (true) {
        try {
          yield call(saga);
          console.error("unexpected root saga termination. The root sagas are supposed to be sagas that live during the whole app lifetime!",saga);
        } catch (e) {
          console.error("Saga error, the saga will be restarted",e);
        }
        yield delay(1000); // Workaround to avoid infinite error loops
      }
    })
  };
};

const rootSagas = [
  domain1saga,
  domain2saga,
  domain3saga,
].map(makeRestartable);

export default function* root() {
  yield rootSagas.map(saga => call(saga));
}
于 2016-09-12T09:43:19.740 回答
16

如何创建rootSaga?

根据 redux-saga [1 , 2]的核心开发人员的说法,创建 rootSaga 的惯用方式是使用all Effect Combinator。另外,请注意从 sagas 中产生数组已被弃用

示例 1

你可以使用这样的东西( +all)

import { fork, all } from 'redux-saga/effects';
import firstSaga from './firstSaga';
import secondSaga from './secondSaga';
import thirdSaga from './thirdSaga';

export default function* rootSaga() {
    yield all([
        fork(firstSaga),
        fork(secondSaga),
        fork(thirdSaga),
    ]);
}

示例 2

取自这里

// foo.js
import { takeEvery } from 'redux-saga/effects';
export const fooSagas = [
  takeEvery("FOO_A", fooASaga),
  takeEvery("FOO_B", fooBSaga),
]

// bar.js
import { takeEvery } from 'redux-saga/effects';
export const barSagas = [
  takeEvery("BAR_A", barASaga),
  takeEvery("BAR_B", barBSaga),
];

// index.js
import { fooSagas } from './foo';
import { barSagas } from './bar';

export default function* rootSaga() {
  yield all([
    ...fooSagas,
    ...barSagas
  ])
}

分叉与产卵

forkspawn都将返回Task对象。分叉任务附加到父任务,而衍生任务与父任务分离

  • 叉子中的错误处理 [链接]:

    来自子任务的错误会自动出现在它们的父项上。如果任何分叉任务引发未捕获的错误,则父任务将 与子错误一起中止,并且整个父任务的执行树 (即分叉任务+父任务主体表示的主任务,如果它仍在运行)将被取消。

  • 衍生任务中的错误处理 [链接]:

    父级在返回之前不会等待分离任务终止,并且所有可能影响父级或分离任务的事件都是完全独立的(错误,取消)。

基于上述,您可以将 fork 用于“关键任务”任务,“如果此任务失败,请使整个应用程序崩溃”,并生成“非关键”任务,“如果此任务失败,不要传播错误给父母”。

于 2017-12-03T20:47:08.837 回答