组合 Sagas

使用 yield* 为组合 Sagas 提供了一种通畅的方式,但这个方法也有一些局限性:

  • 你可能会想要单独测试嵌套的 Generator。这导致了一些重复的测试代码及重复执行的开销。
    我们不希望执行一个嵌套的 Generator,而仅仅是想确认它是被传入正确的参数来调用。

  • 更重要的是,yield* 只允许任务的顺序组合,所以一次你只能 yield* 一个 Generator。

你可以直接使用 yield 来并行地启动一个或多个子任务。当 yield 一个 call 至 Generator,Saga 将等待 Generator 处理结束,
然后以返回的值恢复执行(或错误从子任务中传播过来,则抛出异常)。

  1. function* fetchPosts() {
  2. yield put( actions.requestPosts() )
  3. const products = yield call(fetchApi, '/products')
  4. yield put( actions.receivePosts(products) )
  5. }
  6. function* watchFetch() {
  7. while ( yield take(FETCH_POSTS) ) {
  8. yield call(fetchPosts) // waits for the fetchPosts task to terminate
  9. }
  10. }

yield 一个队列的嵌套的 Generators,将同时启动这些子 Generators(sub-generators),并等待它们完成。
然后以所有返回的结果恢复执行:

  1. function* mainSaga(getState) {
  2. const results = yield [ call(task1), call(task2), ...]
  3. yield put( showResults(results) )
  4. }

事实上,yield Sagas 并不比 yield 其他 effects(未来的 actions,timeouts,等等)不同。
这意味着你可以使用 effect 合并器将那些 Sagas 和所有其他类型的 Effect 合并。

例如,你可能希望用户在有限的时间内完成一些游戏:

  1. function* game(getState) {
  2. let finished
  3. while(!finished) {
  4. // 必须在 60 秒内完成
  5. const {score, timeout} = yield race({
  6. score : call( play, getState),
  7. timeout : call(delay, 60000)
  8. })
  9. if(!timeout) {
  10. finished = true
  11. yield put( showScore(score) )
  12. }
  13. }
  14. }