启动rootSaga的惯用方法是什么?

时间:2016-09-11 16:01:38

标签: redux-saga

redux-saga项目已经存在很长时间了,但是这个库仍然有很多令人困惑的事情。其中一个是:如何开始你的rootSaga。例如,在beginner tutorial中,rootSaga是由一系列传奇开始的。喜欢这个

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

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

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

启动rootSaga的方法与redux-saga repo中的异步示例相同。但是,如果你查看真实世界和购物卡的例子,你会看到rootSagas那里有一系列分叉的传奇。像这样:

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

另外,如果你在redux-saga问题上阅读了一些讨论,你会看到有些人建议使用spawn而不是fork来为rootSaga保护应用程序完全崩溃,如果你的某个分叉传奇被取消了未处理的例外。

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

2 个答案:

答案 0 :(得分:14)

你可以开始多根传奇。但是任何传奇都有能力自己创造另一个传奇。因此,可以创建一个单根传奇,创造另一个传奇。

您只需要了解错误如何传播到父级传奇。如果你有一个单根传奇和一个子传奇崩溃,默认情况下,错误将传播到将终止的父节点,这也将杀死从这个父节点开始的所有其他传奇。

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

一般情况下,我建议您启动多根传奇,或者您的父传奇使用spawn代替fork,以便在发生崩溃时您的应用仍可使用。请注意,在某些地方忘记捕获错误也很容易。例如,如果单个API请求失败,您通常不希望所有应用都无法使用

修改:我建议您查看https://github.com/yelouafi/redux-saga/issues/570

在这个redux-saga issue中,我展示了启动传奇的不同方法及其对你的应用程序的影响。

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

答案 1 :(得分:11)

如何创建rootSaga?

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

示例1

您可以使用this(+ 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

Taken from here

// 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
  ])
}

fork vs. spawn

forkspawn都会返回Task个对象。分叉任务附加到父级,而衍生任务从父级分离

  • 分叉[link]中的错误处理:

      

    来自子任务的错误会自动冒充他们的父母。如果   任何分叉的任务都会引发一个未被捕获的错误,然后父任务将   使用子错误整个父执行树中止   (即分叉任务+父母身体代表的主要任务,如果   它仍然在运行)将被取消。

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

      

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

基于以上所述,您可以使用fork for&#34;任务关键&#34;任务,&#34;如果此任务失败,请崩溃整个应用程序&#34;,并产生&#34;不重要&#34;任务,&#34;如果此任务失败,请不要将错误传播给父级&#34;。